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

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <tsteur@users.noreply.github.com>2016-08-29 04:30:52 +0300
committerGitHub <noreply@github.com>2016-08-29 04:30:52 +0300
commit0c9c30b731ccbacf47e154b9f7a590af49e3d799 (patch)
treec5cf2f6437bb2ee7f3675350ee4b0b6acbbae7d3 /plugins
parentbfdf0bed670f247bf9b1d466e3bcf651e98ab634 (diff)
Better UI for Piwik 3, more responsive, faster, lots of other fixes (#10397)
* improved ui and responsiveness * improve rss widget * commit changes for ui again, got lost after the last commit * fix more tests * restoring files * fix fonts * fix more tests * more test fixes * fix some system tests * fix tests * fix system and ui tests * fix updater tests * make a page as loaded once the callback is called * enable verbose * more verbose output * enable phantomjs debug flag * debug should be a phantomjs option * trying to fix installation tests * fixes #10173 to not compile css files as less * trying to minimize js/css requests to hopefully prevent random ui test fails * disable verbose mode * fix updater and installation * lots of bugfixes and ui tweaks * fix reset dashboard * various bugfixes * fix integration tests * fix text color * hoping to fix installation tests this way * cache css/js resources for an hour, should speed up tests and prevent some random issues * we need to avoid installing plugins multiple times at the same time when requesting resources * finally getting the colors right again * fix most tests, more tests for theme * use an h2 element for titles for better accessibility * fix headline color * use actual theme text color (piwik-black) * fix small font size was applied on all p elements * fix tests * now improving all the datatables * trying to ignore images for visitor log * Revert "trying to ignore images for visitor log" This reverts commit ad1ff7267aae14ad905bef130e956c8593c4fb22. * fix tests * fix we had always ignored a max label width * trying to fix file permissions * fix more file permissions * Improved plugins update API (#10028) * refs #7983 let plugins add or remove fields to websites and better settings api * * Hide CorePluginsAdmin API methods * More documentation * Added some more tests * improved updates API for plugins * better error code as duplicate column cannot really happen when not actually renaming a colum Conflicts: core/Updates/3.0.0-b1.php plugins/CoreUpdater/Commands/Update/CliUpdateObserver.php * fix DB field piwik_log_visit.location_provider too small (#10003) * fixes #9564 fix DB field piwik_log_visit.location_provider too small * use new plugins updater API * DB field piwik_log_visit.visit_total_actions too small (#10002) * fixes #9565 DB field piwik_log_visit.visit_total_actions too small * change type of some db columns that are too small * fix tests (#10040) Conflicts: plugins/CoreAdminHome/Menu.php plugins/Goals/Menu.php plugins/MobileMessaging/Menu.php plugins/SitesManager/Menu.php plugins/UsersManager/Menu.php tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getWidgetMetadata.xml * fix more file permissions * repair more file permissions * repair more file permissions * trying to make ui tests work again, the table was missing * fix some encoding issues * cross browser fixes and usability improvement * move back the config icon, need to find a better solution later * more cross browser fixes * bugfixes * fix ui tests * fix encoding issue * fix various issues with the ui tests when a test gets aborted * also skip this visitor log test when aborted * there were 3 css files that were loaded separately, merge them instead into one css * forgot to add the actual manifest * do not add manifest if custom logo is specified * load font css files first as it was before merging them into big css * fix link icon was not aligned anymore * minor fixes * setting it back to 4px * in popovers the font variable was always ignored and a different font loaded * forgot to update screenshots * fix remaining tests * this should fix an update error * added 3 new widgets system check, system summary and plugin updates * tweak new widgets content * no page reload when changing date or segment * in admin home show only enabled widgets * refs #10295 use getMockBuilder instead of deprecated getMock * fix some ui tests * fix various bugs * fix more tests * fix ui tests * add a space between loading image and loading message * fix docs so they appear on developer.piwik.org * improved documentation * introduce new Widget::renderTemplate method for consistency with controllers * remove no longer needed files * testing system fonts * fix strong was not really bold * more useful system summary * remove ubuntu font * fix most tests and removed most em elements * fix tests * fix headline was very thin * update submodule * update submodules * update submodule * fix failing ui tests * update submodules
Diffstat (limited to 'plugins')
-rw-r--r--plugins/API/Reports/Get.php8
-rw-r--r--plugins/API/stylesheets/listAllAPI.less15
-rw-r--r--plugins/API/templates/listAllAPI.twig33
-rw-r--r--plugins/Actions/Actions.php6
-rw-r--r--plugins/Actions/javascripts/actionsDataTable.js5
-rw-r--r--plugins/Actions/stylesheets/dataTableActions.less4
-rwxr-xr-xplugins/Annotations/javascripts/annotations.js1
-rwxr-xr-xplugins/Annotations/stylesheets/annotations.less50
-rwxr-xr-xplugins/Annotations/templates/_annotation.twig8
-rwxr-xr-xplugins/Annotations/templates/_annotationList.twig10
m---------plugins/AnonymousPiwikUsageMeasurement0
-rw-r--r--plugins/Contents/Contents.php6
-rw-r--r--plugins/Contents/stylesheets/datatable.less3
-rw-r--r--plugins/CoreAdminHome/API.php55
-rw-r--r--plugins/CoreAdminHome/Controller.php142
-rw-r--r--plugins/CoreAdminHome/CoreAdminHome.php5
-rw-r--r--plugins/CoreAdminHome/Menu.php23
-rw-r--r--plugins/CoreAdminHome/angularjs/archiving/archiving.controller.js45
-rw-r--r--plugins/CoreAdminHome/angularjs/branding/branding.controller.js108
-rw-r--r--plugins/CoreAdminHome/angularjs/smtp/mail-smtp.controller.js50
-rw-r--r--plugins/CoreAdminHome/angularjs/trackingcode/imagetrackingcode.controller.js129
-rw-r--r--plugins/CoreAdminHome/angularjs/trackingcode/jstrackingcode.controller.js151
-rw-r--r--plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.controller.js61
-rw-r--r--plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.directive.js36
-rw-r--r--plugins/CoreAdminHome/javascripts/generalSettings.js176
-rw-r--r--plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js320
-rw-r--r--plugins/CoreAdminHome/lang/en.json4
-rw-r--r--plugins/CoreAdminHome/stylesheets/generalSettings.less125
-rw-r--r--plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css36
-rw-r--r--plugins/CoreAdminHome/templates/generalSettings.twig418
-rw-r--r--plugins/CoreAdminHome/templates/home.twig79
-rw-r--r--plugins/CoreAdminHome/templates/trackingCodeGenerator.twig416
-rw-r--r--plugins/CoreHome/Columns/UserId.php8
-rw-r--r--plugins/CoreHome/Controller.php6
-rw-r--r--plugins/CoreHome/CoreHome.php42
-rw-r--r--plugins/CoreHome/DataTableRowAction/RowEvolution.php1
-rw-r--r--plugins/CoreHome/Menu.php8
-rw-r--r--plugins/CoreHome/Widgets/GetDonateForm.php9
-rw-r--r--plugins/CoreHome/Widgets/GetSystemSummary.php79
-rw-r--r--plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js17
-rw-r--r--plugins/CoreHome/angularjs/activity-indicator/activityindicator.html2
-rw-r--r--plugins/CoreHome/angularjs/alert/alert.directive.html2
-rw-r--r--plugins/CoreHome/angularjs/alert/alert.directive.js26
-rw-r--r--plugins/CoreHome/angularjs/alert/alert.directive.less124
-rw-r--r--plugins/CoreHome/angularjs/common/directives/attributes.js26
-rw-r--r--plugins/CoreHome/angularjs/common/directives/dialog.js14
-rw-r--r--plugins/CoreHome/angularjs/common/directives/dropdown-button.js37
-rw-r--r--plugins/CoreHome/angularjs/common/directives/field-condition.js89
-rw-r--r--plugins/CoreHome/angularjs/common/directives/select-on-focus.js63
-rw-r--r--plugins/CoreHome/angularjs/common/directives/side-nav.js49
-rw-r--r--plugins/CoreHome/angularjs/common/services/piwik-api.js15
-rw-r--r--plugins/CoreHome/angularjs/content-block/content-block.directive.html10
-rw-r--r--plugins/CoreHome/angularjs/content-block/content-block.directive.js75
-rw-r--r--plugins/CoreHome/angularjs/content-intro/content-intro.directive.js26
-rw-r--r--plugins/CoreHome/angularjs/content-table/content-table.directive.js30
-rw-r--r--plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.html2
-rw-r--r--plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.js10
-rw-r--r--plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less11
-rw-r--r--plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html8
-rw-r--r--plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less19
-rw-r--r--plugins/CoreHome/angularjs/notification/notification.directive.less7
-rw-r--r--plugins/CoreHome/angularjs/progressbar/progressbar.directive.html7
-rw-r--r--plugins/CoreHome/angularjs/progressbar/progressbar.directive.js54
-rw-r--r--plugins/CoreHome/angularjs/progressbar/progressbar.directive.less3
-rw-r--r--plugins/CoreHome/angularjs/quick-access/quick-access.controller.js4
-rw-r--r--plugins/CoreHome/angularjs/quick-access/quick-access.directive.html54
-rw-r--r--plugins/CoreHome/angularjs/quick-access/quick-access.directive.js2
-rw-r--r--plugins/CoreHome/angularjs/quick-access/quick-access.directive.less9
-rw-r--r--plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js14
-rw-r--r--plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html28
-rw-r--r--plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js37
-rw-r--r--plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html10
-rw-r--r--plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less24
-rw-r--r--plugins/CoreHome/angularjs/selector/selector.directive.js20
-rw-r--r--plugins/CoreHome/angularjs/selector/selector.directive.less9
-rw-r--r--plugins/CoreHome/angularjs/siteselector/siteselector.controller.js1
-rw-r--r--plugins/CoreHome/angularjs/siteselector/siteselector.directive.html33
-rw-r--r--plugins/CoreHome/angularjs/siteselector/siteselector.directive.js15
-rw-r--r--plugins/CoreHome/angularjs/siteselector/siteselector.directive.less17
-rw-r--r--plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html4
-rw-r--r--plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.less64
-rw-r--r--plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js17
-rw-r--r--plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html5
-rw-r--r--plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js46
-rw-r--r--plugins/CoreHome/angularjs/widget/widget.directive.html13
-rw-r--r--plugins/CoreHome/javascripts/calendar.js166
-rwxr-xr-xplugins/CoreHome/javascripts/corehome.js69
-rw-r--r--plugins/CoreHome/javascripts/dataTable.js559
-rw-r--r--plugins/CoreHome/javascripts/iframeResizer.min.js9
-rw-r--r--plugins/CoreHome/javascripts/manifest.json14
-rw-r--r--plugins/CoreHome/javascripts/popover.js5
-rw-r--r--plugins/CoreHome/javascripts/top_controls.js8
-rw-r--r--plugins/CoreHome/javascripts/zen-mode.js25
-rw-r--r--plugins/CoreHome/lang/en.json10
-rwxr-xr-xplugins/CoreHome/stylesheets/_donate.less11
-rw-r--r--plugins/CoreHome/stylesheets/cloud.less1
-rw-r--r--plugins/CoreHome/stylesheets/coreHome.less50
-rw-r--r--plugins/CoreHome/stylesheets/dataTable.less7
-rw-r--r--plugins/CoreHome/stylesheets/dataTable/_dataTable.less468
-rw-r--r--plugins/CoreHome/stylesheets/dataTable/_entityTable.less122
-rw-r--r--plugins/CoreHome/stylesheets/dataTable/_limitSelection.less90
-rw-r--r--plugins/CoreHome/stylesheets/dataTable/_reportDocumentation.less5
-rw-r--r--plugins/CoreHome/stylesheets/dataTable/_rowActions.less12
-rw-r--r--plugins/CoreHome/stylesheets/dataTable/_tableConfiguration.less68
-rw-r--r--plugins/CoreHome/stylesheets/layout.less226
-rw-r--r--plugins/CoreHome/stylesheets/notification.less2
-rw-r--r--plugins/CoreHome/stylesheets/promo.less3
-rw-r--r--plugins/CoreHome/stylesheets/zen-mode.less84
-rw-r--r--plugins/CoreHome/templates/_dataTable.twig41
-rw-r--r--plugins/CoreHome/templates/_dataTableActions.twig114
-rw-r--r--plugins/CoreHome/templates/_dataTableFooter.twig159
-rwxr-xr-xplugins/CoreHome/templates/_donate.twig4
-rw-r--r--plugins/CoreHome/templates/_headerMessage.twig2
-rw-r--r--plugins/CoreHome/templates/_logo.twig15
-rw-r--r--plugins/CoreHome/templates/_menu.twig40
-rw-r--r--plugins/CoreHome/templates/_periodSelect.twig61
-rw-r--r--plugins/CoreHome/templates/_topBar.twig25
-rw-r--r--plugins/CoreHome/templates/_topScreen.twig20
-rw-r--r--plugins/CoreHome/templates/getSystemSummary.twig28
-rwxr-xr-xplugins/CoreHome/templates/widgetContainer.twig11
-rw-r--r--plugins/CoreHome/tests/Integration/Tracker/VisitRequestProcessorTest.php4
-rw-r--r--plugins/CorePluginsAdmin/Controller.php17
-rw-r--r--plugins/CorePluginsAdmin/CorePluginsAdmin.php5
-rw-r--r--plugins/CorePluginsAdmin/Marketplace.php8
-rw-r--r--plugins/CorePluginsAdmin/MarketplaceApiClient.php7
-rw-r--r--plugins/CorePluginsAdmin/Menu.php2
-rw-r--r--plugins/CorePluginsAdmin/SettingsMetadata.php1
-rw-r--r--plugins/CorePluginsAdmin/Tasks.php15
-rw-r--r--plugins/CorePluginsAdmin/UpdateCommunication.php35
-rw-r--r--plugins/CorePluginsAdmin/Widgets/GetNewPlugins.php56
-rw-r--r--plugins/CorePluginsAdmin/angularjs/field/field.directive.html3
-rw-r--r--plugins/CorePluginsAdmin/angularjs/field/field.directive.js141
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox-array.html16
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox.html10
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-file.html10
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-hidden.html6
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html9
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html17
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-select.html8
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-site.html11
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-text-array.html10
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-text.html12
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-textarea-array.html9
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-textarea.html8
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html107
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js260
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form/form.directive.js39
-rw-r--r--plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.controller.js23
-rw-r--r--plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.directive.js59
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js35
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html34
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less3
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugins/plugin-filter.directive.js112
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugins/plugin-management.directive.js85
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugins/plugin-name.directive.js65
-rw-r--r--plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.html8
-rw-r--r--plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.js31
-rwxr-xr-xplugins/CorePluginsAdmin/javascripts/marketplace.js50
-rw-r--r--plugins/CorePluginsAdmin/javascripts/pluginExtend.js31
-rw-r--r--plugins/CorePluginsAdmin/javascripts/pluginOverview.js45
-rw-r--r--plugins/CorePluginsAdmin/javascripts/plugins.js93
-rw-r--r--plugins/CorePluginsAdmin/lang/en.json13
-rw-r--r--plugins/CorePluginsAdmin/stylesheets/marketplace-widget.less44
-rw-r--r--plugins/CorePluginsAdmin/stylesheets/marketplace.less33
-rw-r--r--plugins/CorePluginsAdmin/stylesheets/plugin-details.less78
-rw-r--r--plugins/CorePluginsAdmin/stylesheets/plugins_admin.less34
-rw-r--r--plugins/CorePluginsAdmin/templates/getNewPlugins.twig22
-rw-r--r--plugins/CorePluginsAdmin/templates/getNewPluginsAdmin.twig32
-rw-r--r--plugins/CorePluginsAdmin/templates/macros.twig356
-rw-r--r--plugins/CorePluginsAdmin/templates/marketplace.twig82
-rw-r--r--plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig120
-rw-r--r--plugins/CorePluginsAdmin/templates/pluginDetails.twig60
-rw-r--r--plugins/CorePluginsAdmin/templates/plugins.twig39
-rw-r--r--plugins/CorePluginsAdmin/templates/themes.twig36
-rw-r--r--plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php38
-rw-r--r--plugins/CoreUpdater/Controller.php61
-rw-r--r--plugins/CoreUpdater/CoreUpdater.php21
-rw-r--r--plugins/CoreUpdater/SystemSettings.php109
-rw-r--r--plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php4
-rw-r--r--plugins/CoreUpdater/stylesheets/updateLayout.css15
-rw-r--r--plugins/CoreUpdater/templates/layout.twig29
-rw-r--r--plugins/CoreUpdater/templates/newVersionAvailable.twig4
-rw-r--r--plugins/CoreUpdater/templates/runUpdaterAndExit_welcome.twig8
-rw-r--r--plugins/CoreUpdater/templates/updateHttpsError.twig8
-rw-r--r--plugins/CoreUpdater/templates/updateSuccess.twig8
-rw-r--r--plugins/CoreVisualizations/Visualizations/Cloud.php2
-rw-r--r--plugins/CoreVisualizations/Visualizations/HtmlTable.php2
-rw-r--r--plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php2
-rw-r--r--plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php2
-rw-r--r--plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php2
-rw-r--r--plugins/CoreVisualizations/Visualizations/Sparklines.php7
-rw-r--r--plugins/CoreVisualizations/javascripts/jqplot.js10
-rw-r--r--plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less26
-rw-r--r--plugins/CoreVisualizations/stylesheets/jqplot.css8
-rw-r--r--plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig61
m---------plugins/CustomAlerts0
m---------plugins/CustomDimensions0
-rw-r--r--plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html78
-rw-r--r--plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.pngbin11572 -> 14864 bytes
-rw-r--r--plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.pngbin68534 -> 102281 bytes
-rw-r--r--plugins/DBStats/Controller.php2
-rw-r--r--plugins/DBStats/DBStats.php8
-rw-r--r--plugins/DBStats/Reports/GetAdminDataSummary.php6
-rw-r--r--plugins/DBStats/Reports/GetDatabaseUsageSummary.php6
-rw-r--r--plugins/DBStats/Reports/GetIndividualMetricsSummary.php6
-rw-r--r--plugins/DBStats/Reports/GetIndividualReportsSummary.php6
-rw-r--r--plugins/DBStats/Reports/GetTrackerDataSummary.php6
-rw-r--r--plugins/DBStats/stylesheets/dbStatsTable.less25
-rwxr-xr-xplugins/DBStats/templates/index.twig144
-rw-r--r--plugins/Dashboard/Dashboard.php4
-rw-r--r--plugins/Dashboard/javascripts/dashboard.js5
-rw-r--r--plugins/Dashboard/javascripts/dashboardObject.js83
-rwxr-xr-xplugins/Dashboard/javascripts/dashboardWidget.js14
-rw-r--r--plugins/Dashboard/javascripts/widgetMenu.js2
-rw-r--r--plugins/Dashboard/stylesheets/dashboard.less95
-rw-r--r--plugins/Dashboard/stylesheets/standalone.css7
-rw-r--r--plugins/Dashboard/stylesheets/widget.less43
-rw-r--r--plugins/Dashboard/templates/_dashboardSettings.twig44
-rw-r--r--plugins/Dashboard/templates/embeddedIndex.twig53
-rw-r--r--plugins/DevicesDetection/templates/detection.twig126
-rw-r--r--plugins/DevicesDetection/templates/list.twig2
-rw-r--r--plugins/Diagnostics/Diagnostic/GdExtensionCheck.php2
-rw-r--r--plugins/Diagnostics/stylesheets/configfile.less5
-rw-r--r--plugins/Diagnostics/templates/configfile.twig6
-rw-r--r--plugins/Ecommerce/Controller.php6
-rw-r--r--plugins/Ecommerce/Widgets/GetEcommerceLog.php9
-rw-r--r--plugins/Ecommerce/templates/conversionOverview.twig41
-rw-r--r--plugins/Ecommerce/templates/getSparklines.twig4
-rw-r--r--plugins/ExamplePlugin/Widgets/MyExampleWidget.php5
-rw-r--r--plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePage.pngbin14782 -> 20115 bytes
-rw-r--r--plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePagePartial.pngbin9848 -> 15636 bytes
-rw-r--r--plugins/ExampleRssWidget/stylesheets/rss.less2
-rw-r--r--plugins/ExampleSettingsPlugin/SystemSettings.php1
-rw-r--r--plugins/ExampleTheme/stylesheets/theme.less11
-rw-r--r--plugins/Feedback/angularjs/ratefeature/ratefeature.directive.html1
-rw-r--r--plugins/Feedback/stylesheets/feedback.less15
-rw-r--r--plugins/Feedback/templates/index.twig75
-rw-r--r--plugins/Goals/Controller.php34
-rw-r--r--plugins/Goals/Goals.php4
-rw-r--r--plugins/Goals/Menu.php2
-rw-r--r--plugins/Goals/Pages.php2
-rw-r--r--plugins/Goals/Reports/Get.php49
-rw-r--r--plugins/Goals/Visualizations/Goals.php2
-rw-r--r--plugins/Goals/Widgets/AddNewGoal.php10
-rw-r--r--plugins/Goals/Widgets/EditGoals.php10
-rw-r--r--plugins/Goals/angularjs/manage-goals/manage-goals.controller.js171
-rw-r--r--plugins/Goals/angularjs/manage-goals/manage-goals.directive.js38
-rw-r--r--plugins/Goals/javascripts/goalsForm.js231
-rw-r--r--plugins/Goals/lang/en.json3
-rw-r--r--plugins/Goals/stylesheets/goals.css17
-rw-r--r--plugins/Goals/templates/_addEditGoal.twig69
-rw-r--r--plugins/Goals/templates/_formAddGoal.twig248
-rw-r--r--plugins/Goals/templates/_listGoalEdit.twig42
-rw-r--r--plugins/Goals/templates/addNewGoal.twig23
-rw-r--r--plugins/Goals/templates/conversionOverview.twig35
-rw-r--r--plugins/Goals/templates/manageGoals.twig16
-rw-r--r--plugins/Insights/Visualizations/Insight.php2
-rw-r--r--plugins/Insights/javascripts/insightsDataTable.js38
-rw-r--r--plugins/Insights/stylesheets/insightVisualization.less11
-rw-r--r--plugins/Insights/templates/insightControls.twig84
-rw-r--r--plugins/Insights/templates/table_row.twig8
-rw-r--r--plugins/Installation/Controller.php57
-rw-r--r--plugins/Installation/FormDatabaseSetup.php2
-rw-r--r--plugins/Installation/FormFirstWebsiteSetup.php2
-rw-r--r--plugins/Installation/FormSuperUser.php2
-rw-r--r--plugins/Installation/Installation.php2
-rw-r--r--plugins/Installation/ServerFilesGenerator.php2
-rw-r--r--plugins/Installation/Widgets/GetSystemCheck.php80
-rw-r--r--plugins/Installation/javascripts/installation.js18
-rw-r--r--plugins/Installation/lang/en.json2
-rw-r--r--plugins/Installation/stylesheets/installation.css19
-rwxr-xr-xplugins/Installation/stylesheets/systemCheckPage.less30
-rwxr-xr-xplugins/Installation/templates/_systemCheckSection.twig4
-rw-r--r--plugins/Installation/templates/getSystemCheckWidget.twig39
-rw-r--r--plugins/Installation/templates/layout.twig30
-rwxr-xr-xplugins/Installation/templates/systemCheckPage.twig36
-rw-r--r--plugins/LanguagesManager/LanguagesManager.php6
-rw-r--r--plugins/LanguagesManager/angularjs/translationsearch/translationsearch.directive.html6
-rw-r--r--plugins/LanguagesManager/templates/getLanguagesSelector.twig36
-rw-r--r--plugins/LanguagesManager/templates/searchTranslation.twig6
-rw-r--r--plugins/Live/Visualizations/VisitorLog.php2
-rw-r--r--plugins/Live/javascripts/live.js2
-rw-r--r--plugins/Live/javascripts/visitorLog.js2
-rw-r--r--plugins/Live/stylesheets/live.less13
-rw-r--r--plugins/Live/templates/_dataTableViz_visitorLog.twig8
-rw-r--r--plugins/Live/templates/getLastVisitsStart.twig4
-rw-r--r--plugins/Live/templates/index.twig5
-rw-r--r--plugins/Live/templates/indexVisitorLog.twig4
m---------plugins/LogViewer0
-rwxr-xr-xplugins/Login/javascripts/login.js47
-rw-r--r--plugins/Login/lang/en.json6
-rw-r--r--plugins/Login/stylesheets/login.less134
-rw-r--r--plugins/Login/templates/login.twig232
m---------plugins/LoginHttpAuth0
-rw-r--r--plugins/MobileMessaging/API.php3
-rw-r--r--plugins/MobileMessaging/Controller.php67
-rw-r--r--plugins/MobileMessaging/Menu.php12
-rw-r--r--plugins/MobileMessaging/MobileMessaging.php26
-rw-r--r--plugins/MobileMessaging/SMSProvider/Clockwork.php2
-rw-r--r--plugins/MobileMessaging/angularjs/delegate-mobile-messaging-settings/delegate-mobile-messaging-settings.controller.js39
-rw-r--r--plugins/MobileMessaging/angularjs/manage-mobile-phone-numbers/manage-mobile-phone-numbers.controller.js110
-rw-r--r--plugins/MobileMessaging/angularjs/manage-sms-provider/manage-sms-provider.controller.js64
-rw-r--r--plugins/MobileMessaging/javascripts/MobileMessagingSettings.js266
-rw-r--r--plugins/MobileMessaging/lang/en.json6
-rw-r--r--plugins/MobileMessaging/stylesheets/MobileMessagingSettings.less39
-rw-r--r--plugins/MobileMessaging/templates/index.twig189
-rw-r--r--plugins/MobileMessaging/templates/macros.twig90
-rw-r--r--plugins/MobileMessaging/templates/reportParametersScheduledReports.twig92
-rw-r--r--plugins/MobileMessaging/templates/userSettings.twig129
-rw-r--r--plugins/MobileMessaging/tests/Integration/MobileMessagingTest.php5
-rw-r--r--plugins/Morpheus/Menu.php7
-rw-r--r--plugins/Morpheus/images/logo.svg10
-rw-r--r--plugins/Morpheus/javascripts/ajaxHelper.js3
-rw-r--r--plugins/Morpheus/javascripts/jquery.icheck.min.js8
-rw-r--r--plugins/Morpheus/javascripts/layout.js11
-rw-r--r--plugins/Morpheus/javascripts/morpheus.js33
-rw-r--r--plugins/Morpheus/javascripts/piwikHelper.js114
-rw-r--r--plugins/Morpheus/stylesheets/base.less6
-rwxr-xr-xplugins/Morpheus/stylesheets/base/bootstrap.css719
-rw-r--r--plugins/Morpheus/stylesheets/base/font.css0
-rw-r--r--plugins/Morpheus/stylesheets/base/icons.css4
-rw-r--r--plugins/Morpheus/stylesheets/base/mixins.less5
-rw-r--r--plugins/Morpheus/stylesheets/general/_admin.less29
-rw-r--r--plugins/Morpheus/stylesheets/general/_form.less86
-rw-r--r--plugins/Morpheus/stylesheets/general/_forms.less524
-rw-r--r--plugins/Morpheus/stylesheets/general/_jqueryUI.less18
-rw-r--r--plugins/Morpheus/stylesheets/general/_misc.less6
-rw-r--r--plugins/Morpheus/stylesheets/general/_typography.less36
-rw-r--r--plugins/Morpheus/stylesheets/ieonly.css37
-rw-r--r--plugins/Morpheus/stylesheets/main.less195
-rw-r--r--plugins/Morpheus/stylesheets/theme-advanced.less8
-rw-r--r--plugins/Morpheus/stylesheets/theme.less13
-rw-r--r--plugins/Morpheus/stylesheets/ui/_alerts.less64
-rw-r--r--plugins/Morpheus/stylesheets/ui/_buttons.less48
-rw-r--r--plugins/Morpheus/stylesheets/ui/_cards.less45
-rw-r--r--plugins/Morpheus/stylesheets/ui/_charts.less4
-rw-r--r--plugins/Morpheus/stylesheets/ui/_code.less5
-rw-r--r--plugins/Morpheus/stylesheets/ui/_components.less30
-rw-r--r--plugins/Morpheus/stylesheets/ui/_tabs.less23
-rw-r--r--plugins/Morpheus/stylesheets/uibase/_header.less21
-rw-r--r--plugins/Morpheus/stylesheets/uibase/_headerMessage.less5
-rw-r--r--plugins/Morpheus/stylesheets/uibase/_languageSelect.less16
-rw-r--r--plugins/Morpheus/stylesheets/uibase/_periodSelect.less32
-rw-r--r--plugins/Morpheus/templates/admin.twig34
-rw-r--r--plugins/Morpheus/templates/contentBlock.twig13
-rw-r--r--plugins/Morpheus/templates/dashboard.twig20
-rw-r--r--plugins/Morpheus/templates/demo.twig520
-rw-r--r--plugins/Morpheus/templates/genericForm.twig32
-rw-r--r--plugins/Morpheus/templates/layout.twig7
-rw-r--r--plugins/Morpheus/templates/settingsMacros.twig137
-rw-r--r--plugins/MultiSites/angularjs/dashboard/dashboard-model.service.js1
-rw-r--r--plugins/MultiSites/angularjs/dashboard/dashboard.directive.html59
-rw-r--r--plugins/MultiSites/angularjs/dashboard/dashboard.directive.less103
-rw-r--r--plugins/MultiSites/templates/getSitesInfo.twig24
-rw-r--r--plugins/Overlay/javascripts/Piwik_Overlay.js4
-rw-r--r--plugins/Overlay/stylesheets/overlay.css6
-rw-r--r--plugins/PrivacyManager/API.php146
-rw-r--r--plugins/PrivacyManager/Controller.php124
-rw-r--r--plugins/PrivacyManager/Menu.php2
-rw-r--r--plugins/PrivacyManager/PrivacyManager.php13
-rw-r--r--plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js38
-rw-r--r--plugins/PrivacyManager/angularjs/delete-old-logs/delete-old-logs.controller.js54
-rw-r--r--plugins/PrivacyManager/angularjs/delete-old-reports/delete-old-reports.controller.js66
-rw-r--r--plugins/PrivacyManager/angularjs/do-not-track-preference/do-not-track-preference.controller.js40
-rw-r--r--plugins/PrivacyManager/angularjs/report-deletion.model.js111
-rw-r--r--plugins/PrivacyManager/angularjs/schedule-report-deletion/schedule-report-deletion.controller.js65
-rw-r--r--plugins/PrivacyManager/javascripts/privacySettings.js212
-rw-r--r--plugins/PrivacyManager/templates/privacySettings.twig455
-rw-r--r--plugins/Provider/Updates/3.0.0-b1.php2
m---------plugins/QueuedTracking0
-rw-r--r--plugins/Referrers/Controller.php17
-rw-r--r--plugins/Referrers/Referrers.php2
-rw-r--r--plugins/SEO/Widgets/GetRank.php11
-rw-r--r--plugins/SEO/templates/getRank.twig16
-rw-r--r--plugins/ScheduledReports/Controller.php15
-rw-r--r--plugins/ScheduledReports/ScheduledReports.php3
-rw-r--r--plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.controller.js194
-rw-r--r--plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.directive.js30
-rw-r--r--plugins/ScheduledReports/javascripts/pdf.js203
-rw-r--r--plugins/ScheduledReports/lang/en.json2
-rw-r--r--plugins/ScheduledReports/stylesheets/scheduledreports.less16
-rw-r--r--plugins/ScheduledReports/templates/_addReport.twig307
-rw-r--r--plugins/ScheduledReports/templates/_listReports.twig36
-rw-r--r--plugins/ScheduledReports/templates/index.twig8
-rw-r--r--plugins/ScheduledReports/templates/reportParametersScheduledReports.twig118
-rw-r--r--plugins/ScheduledReports/tests/Integration/ApiTest.php7
m---------plugins/SecurityInfo0
-rw-r--r--plugins/SegmentEditor/SegmentEditor.php2
-rw-r--r--plugins/SegmentEditor/javascripts/Segmentation.js116
-rw-r--r--plugins/SegmentEditor/stylesheets/segmentation.less56
-rw-r--r--plugins/SegmentEditor/templates/_segmentSelector.twig29
-rw-r--r--plugins/SitesManager/Controller.php22
-rw-r--r--plugins/SitesManager/Menu.php14
-rw-r--r--plugins/SitesManager/SitesManager.php1
-rw-r--r--plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js17
-rw-r--r--plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js29
-rw-r--r--plugins/SitesManager/lang/en.json2
-rw-r--r--plugins/SitesManager/stylesheets/SitesManager.less4
-rw-r--r--plugins/SitesManager/templates/_displayJavascriptCode.twig2
-rw-r--r--plugins/SitesManager/templates/global-settings.html130
-rw-r--r--plugins/SitesManager/templates/globalSettings.twig9
-rw-r--r--plugins/SitesManager/templates/help/excluded-ip-help.html2
-rw-r--r--plugins/SitesManager/templates/help/excluded-query-parameters-help.html2
-rw-r--r--plugins/SitesManager/templates/help/excluded-user-agents-help.html2
-rw-r--r--plugins/SitesManager/templates/help/timezone-help.html2
-rw-r--r--plugins/SitesManager/templates/index.html1
-rw-r--r--plugins/SitesManager/templates/measurable_type_settings.twig7
-rw-r--r--plugins/SitesManager/templates/sites-list/add-entity-dialog.html16
-rw-r--r--plugins/SitesManager/templates/sites-list/site-fields.html48
-rw-r--r--plugins/SitesManager/templates/sites-manager-header.html32
-rw-r--r--plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml2
m---------plugins/TasksTimetable0
-rw-r--r--plugins/TestRunner/Commands/TestsRunUI.php10
m---------plugins/TreemapVisualization0
-rw-r--r--plugins/UserCountry/Controller.php13
-rwxr-xr-xplugins/UserCountry/LocationProvider/DefaultProvider.php6
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp/Pecl.php12
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp/Php.php12
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp/ServerBased.php20
-rw-r--r--plugins/UserCountry/Menu.php2
-rw-r--r--plugins/UserCountry/UserCountry.php6
-rw-r--r--plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.controller.js73
-rw-r--r--plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.directive.js33
-rw-r--r--plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.controller.js149
-rw-r--r--plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.directive.js37
-rwxr-xr-xplugins/UserCountry/javascripts/userCountry.js206
-rwxr-xr-xplugins/UserCountry/stylesheets/userCountry.less52
-rwxr-xr-xplugins/UserCountry/templates/_updaterManage.twig120
-rw-r--r--plugins/UserCountry/templates/_updaterNextRunTime.twig2
-rwxr-xr-xplugins/UserCountry/templates/adminIndex.twig229
-rw-r--r--plugins/UserCountry/templates/getDistinctCountries.twig12
-rw-r--r--plugins/UserCountryMap/javascripts/realtime-map.js14
-rw-r--r--plugins/UserCountryMap/javascripts/visitor-map.js6
-rw-r--r--plugins/UserCountryMap/stylesheets/realtime-map.less4
-rw-r--r--plugins/UserCountryMap/stylesheets/visitor-map.less1
-rw-r--r--plugins/UserCountryMap/templates/realtimeMap.twig3
-rw-r--r--plugins/UserCountryMap/templates/visitorMap.twig8
-rw-r--r--plugins/UsersManager/Controller.php56
-rw-r--r--plugins/UsersManager/Menu.php4
-rw-r--r--plugins/UsersManager/UsersManager.php9
-rw-r--r--plugins/UsersManager/angularjs/give-user-view-access/give-user-view-access.controller.js167
-rw-r--r--plugins/UsersManager/angularjs/manage-super-user/manage-super-user.controller.js76
-rw-r--r--plugins/UsersManager/angularjs/manage-user-access/manage-user-access.controller.js96
-rw-r--r--plugins/UsersManager/angularjs/manage-users/manage-users.controller.js193
-rw-r--r--plugins/UsersManager/angularjs/personal-settings/anonymous-settings.controller.js47
-rw-r--r--plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js64
-rw-r--r--plugins/UsersManager/javascripts/giveViewAccess.js176
-rw-r--r--plugins/UsersManager/javascripts/usersManager.js312
-rw-r--r--plugins/UsersManager/javascripts/usersSettings.js99
-rw-r--r--plugins/UsersManager/lang/en.json2
-rw-r--r--plugins/UsersManager/stylesheets/usersManager.less21
-rw-r--r--plugins/UsersManager/templates/anonymousSettings.twig75
-rw-r--r--plugins/UsersManager/templates/index.twig263
-rw-r--r--plugins/UsersManager/templates/userSettings.twig174
m---------plugins/VisitorGenerator0
-rw-r--r--plugins/VisitsSummary/Reports/Get.php2
-rw-r--r--plugins/VisitsSummary/templates/_sparklines.twig4
-rw-r--r--plugins/WebsiteMeasurable/MeasurableSettings.php1
-rw-r--r--plugins/Widgetize/Widgetize.php5
-rw-r--r--plugins/Widgetize/angularjs/export-widget/export-widget.controller.js29
-rw-r--r--plugins/Widgetize/angularjs/widget-preview/widget-preview.directive.js98
-rw-r--r--plugins/Widgetize/javascripts/widgetize.js84
-rw-r--r--plugins/Widgetize/stylesheets/widgetize.less15
-rw-r--r--plugins/Widgetize/templates/iframe.twig7
-rw-r--r--plugins/Widgetize/templates/index.twig64
-rw-r--r--plugins/Widgetize/tests/System/WidgetTest.php57
466 files changed, 12238 insertions, 10294 deletions
diff --git a/plugins/API/Reports/Get.php b/plugins/API/Reports/Get.php
index 8adc67b4ed..445ec3cbc3 100644
--- a/plugins/API/Reports/Get.php
+++ b/plugins/API/Reports/Get.php
@@ -27,11 +27,11 @@ class Get extends Report
$this->reportsToMerge = $this->getReportsToMerge();
- $this->module = 'API';
- $this->action = 'get';
+ $this->module = 'API';
+ $this->action = 'get';
- $this->categoryId = 'API';
- $this->name = Piwik::translate('General_MainMetrics');
+ $this->categoryId = 'API';
+ $this->name = Piwik::translate('General_MainMetrics');
$this->documentation = '';
$this->processedMetrics = array();
diff --git a/plugins/API/stylesheets/listAllAPI.less b/plugins/API/stylesheets/listAllAPI.less
index a01161b609..40188e98d3 100644
--- a/plugins/API/stylesheets/listAllAPI.less
+++ b/plugins/API/stylesheets/listAllAPI.less
@@ -1,10 +1,3 @@
-#token_auth {
- background-color: #E8FFE9;
- border: 1px solid #00CC3A;
- margin: 0 0 16px 8px;
- padding: 12px;
- line-height: 4em;
-}
.example, .example A {
color: #9E9E9E;
@@ -14,14 +7,6 @@
padding: 0 15px 0 15px;
}
-.page_api h2 {
- border-bottom: 1px solid #DADADA;
- margin: 10px -15px 15px 0;
- padding: 0 0 5px 0;
- font-size: 24px;
- width:100%;
-}
-
.page_api p {
line-height: 140%;
padding-bottom: 20px;
diff --git a/plugins/API/templates/listAllAPI.twig b/plugins/API/templates/listAllAPI.twig
index c42d313fcf..c84488b1ec 100644
--- a/plugins/API/templates/listAllAPI.twig
+++ b/plugins/API/templates/listAllAPI.twig
@@ -10,22 +10,21 @@
{% block content %}
<div class="api-list">
-
- <h2>{{ title }}</h2>
-
- <p>{{ 'API_PluginDescription'|translate }}</p>
-
- <p>
- {{ '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>")|raw }}
- </p>
-
- <h2>{{ 'API_UserAuthentication'|translate }}</h2>
-
- <p>
- {{ 'API_UsingTokenAuth'|translate('','',"")|raw }}<br/>
- <span id='token_auth'>&amp;token_auth=<strong>{{ token_auth }}</strong></span><br/>
- {{ 'API_KeepTokenSecret'|translate('<b>','</b>')|raw }}
- {{ list_api_methods_with_links|raw }}
- <br/>
+ <div piwik-content-block content-title="{{ title|e('html_attr') }}" rate="true">
+ <p>{{ 'API_PluginDescription'|translate }}</p>
+
+ <p>
+ {{ '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>")|raw }}
+ </p>
+ </div>
+ <div piwik-content-block content-title="{{ 'API_UserAuthentication'|translate|e('html_attr') }}">
+ <p>
+ {{ 'API_UsingTokenAuth'|translate('','',"")|raw }}<br/>
+ <pre piwik-select-on-focus id='token_auth'>&amp;token_auth=<strong>{{ token_auth }}</strong></pre><br/>
+ {{ 'API_KeepTokenSecret'|translate('<b>','</b>')|raw }}
+ </p>
+ </div>
+ {{ list_api_methods_with_links|raw }}
+ <br/>
</div>
{% endblock %} \ No newline at end of file
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index 38ccbc229c..fab89e6344 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -32,7 +32,6 @@ class Actions extends \Piwik\Plugin
{
return array(
'ViewDataTable.configure' => 'configureViewDataTable',
- 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'Insights.addReportToOverview' => 'addReportToInsightsOverview',
'Live.getAllVisitorDetails' => 'extendVisitorDetails',
@@ -97,11 +96,6 @@ class Actions extends \Piwik\Plugin
$reports['Actions_getDownloads'] = array('flat' => 1);
}
- public function getStylesheetFiles(&$stylesheets)
- {
- $stylesheets[] = "plugins/Actions/stylesheets/dataTableActions.less";
- }
-
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/Actions/javascripts/actionsDataTable.js";
diff --git a/plugins/Actions/javascripts/actionsDataTable.js b/plugins/Actions/javascripts/actionsDataTable.js
index ef8442e1bd..1921916340 100644
--- a/plugins/Actions/javascripts/actionsDataTable.js
+++ b/plugins/Actions/javascripts/actionsDataTable.js
@@ -80,15 +80,14 @@
self.dataTableLoaded(response, self.workingDivId);
};
- self.handleSearchBox(domElem, dataTableLoadedProxy);
self.handleConfigurationBox(domElem, dataTableLoadedProxy);
+ self.handleSearchBox(domElem, dataTableLoadedProxy);
}
self.handleColumnDocumentation(domElem);
self.handleRelatedReports(domElem);
self.handleTriggeredEvents(domElem);
self.handleCellTooltips(domElem);
- self.handleExpandFooter(domElem);
self.setFixWidthToMakeEllipsisWork(domElem);
self.handleSummaryRow(domElem);
self.openSubtableFromLevel0IfOnlyOneSubtableGiven(domElem);
@@ -301,6 +300,8 @@
content.trigger('piwik:dataTableLoaded');
+ piwikHelper.compileAngularComponents(content);
+
piwikHelper.lazyScrollTo(content[0], 400);
return content;
diff --git a/plugins/Actions/stylesheets/dataTableActions.less b/plugins/Actions/stylesheets/dataTableActions.less
deleted file mode 100644
index 7c059d948b..0000000000
--- a/plugins/Actions/stylesheets/dataTableActions.less
+++ /dev/null
@@ -1,4 +0,0 @@
-.dataTableActions > .dataTableWrapper {
- width: 500px;
- min-height: 1px;
-} \ No newline at end of file
diff --git a/plugins/Annotations/javascripts/annotations.js b/plugins/Annotations/javascripts/annotations.js
index 90ebee273c..f699b3aec9 100755
--- a/plugins/Annotations/javascripts/annotations.js
+++ b/plugins/Annotations/javascripts/annotations.js
@@ -502,6 +502,7 @@
// reload annotation manager for new date/period
annotationsApi.getAnnotationManager(idSite, date, period, lastN, function (response) {
+
replaceAnnotationManager(manager, response);
createDatePickers(manager);
diff --git a/plugins/Annotations/stylesheets/annotations.less b/plugins/Annotations/stylesheets/annotations.less
index c825b42e43..eef8f9c478 100755
--- a/plugins/Annotations/stylesheets/annotations.less
+++ b/plugins/Annotations/stylesheets/annotations.less
@@ -1,46 +1,41 @@
.evolution-annotations {
position: relative;
- height: 16px;
+ height: 18px;
width: 100%;
- margin-top: 12px;
- margin-bottom: -28px;
cursor: pointer;
}
.evolution-annotations > span {
position: absolute;
- top:10px;
-}
-
-#content, .ui-dialog {
- .evolution-annotations > span {
- top: -1px;
- position: absolute;
- }
- .annotation-manager {
- margin-top: 15px;
- }
}
.evolution-annotations {
- margin-top: -5px;
- margin-bottom: -5px;
-}
-
-body > .widget .evolution-annotations > span {
- top: -25px;
+ margin-top: 3px;
}
.annotation-manager {
text-align: left;
- margin-top: -18px;
+ margin-top: 5px;
.new-annotation-row {
height: 145px;
+ .input-field {
+ margin-top: 2px;
+ .new-annotation-edit {
+ padding-bottom: 4px;
+ }
+ }
}
.annotation.edit .annotation-edit-mode {
min-height: 125px;
+
+ .input-field {
+ margin-top: 2px;
+ .annotation-edit {
+ padding-bottom: 4px;
+ }
+ }
}
}
@@ -177,19 +172,6 @@ body > .widget .evolution-annotations > span {
text-decoration: none;
}
-.annotationView {
- float: right;
- margin-left: 5px;
- position: relative;
- cursor: pointer;
-}
-
-.annotationView > span {
- font-style: italic;
- display: inline-block;
- margin: 4px 4px 0 4px;
-}
-
.annotation-period-edit {
display: inline-block;
background: white;
diff --git a/plugins/Annotations/templates/_annotation.twig b/plugins/Annotations/templates/_annotation.twig
index b021eb578c..ab0a8a2ca6 100755
--- a/plugins/Annotations/templates/_annotation.twig
+++ b/plugins/Annotations/templates/_annotation.twig
@@ -26,10 +26,12 @@
</div>
{% if annotation.canEditOrDelete %}
<div class="annotation-edit-mode" style="display:none;">
- <input class="annotation-edit" type="text" value="{{ annotation.note|raw }}"/>
+ <div class="input-field">
+ <input class="annotation-edit browser-default" type="text" value="{{ annotation.note|raw }}"/>
+ </div>
<br/>
- <input class="annotation-save submit" type="button" value="{{ 'General_Save'|translate }}"/>
- <input class="annotation-cancel submit" type="button" value="{{ 'General_Cancel'|translate }}"/>
+ <input class="annotation-save btn" type="button" value="{{ 'General_Save'|translate }}"/>
+ <input class="annotation-cancel btn" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
{% endif %}
</td>
diff --git a/plugins/Annotations/templates/_annotationList.twig b/plugins/Annotations/templates/_annotationList.twig
index 6628400519..c5a0db951c 100755
--- a/plugins/Annotations/templates/_annotationList.twig
+++ b/plugins/Annotations/templates/_annotationList.twig
@@ -18,9 +18,13 @@
</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 }}"/>
+ <div class="input-field">
+ <input type="text" value=""
+ class="new-annotation-edit browser-default"
+ placeholder="{{ 'Annotations_EnterAnnotationText'|translate }}"/>
+ </div><br/>
+ <input type="button" class="btn new-annotation-save" value="{{ 'General_Save'|translate }}"/>
+ <input type="button" class="btn new-annotation-cancel" value="{{ 'General_Cancel'|translate }}"/>
</td>
<td class="annotation-user-cell"><span class="annotation-user">{{ userLogin }}</span></td>
</tr>
diff --git a/plugins/AnonymousPiwikUsageMeasurement b/plugins/AnonymousPiwikUsageMeasurement
-Subproject 557696c55f5356d066a264402276fdb50dc5714
+Subproject 7b9fd79de3dc980a5c79385876a0d7f7af7aa60
diff --git a/plugins/Contents/Contents.php b/plugins/Contents/Contents.php
index df42522cb3..a217e159c7 100644
--- a/plugins/Contents/Contents.php
+++ b/plugins/Contents/Contents.php
@@ -21,7 +21,6 @@ class Contents extends \Piwik\Plugin
'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations',
'Metrics.getDefaultMetricDocumentationTranslations' => 'addMetricDocumentationTranslations',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
- 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles'
);
}
@@ -37,11 +36,6 @@ class Contents extends \Piwik\Plugin
$jsFiles[] = "plugins/Contents/javascripts/contentsDataTable.js";
}
- public function getStylesheetFiles(&$stylesheets)
- {
- $stylesheets[] = "plugins/Contents/stylesheets/datatable.less";
- }
-
public function addMetricDocumentationTranslations(&$translations)
{
$translations['nb_impressions'] = Piwik::translate('Contents_ImpressionsMetricDocumentation');
diff --git a/plugins/Contents/stylesheets/datatable.less b/plugins/Contents/stylesheets/datatable.less
deleted file mode 100644
index 2ecce8d2ee..0000000000
--- a/plugins/Contents/stylesheets/datatable.less
+++ /dev/null
@@ -1,3 +0,0 @@
-#content:not(.admin) .ContentsDataTable > .dataTableWrapper {
- width: 750px;
-} \ No newline at end of file
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index 18f5764a5a..a3dc0292fa 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -11,6 +11,8 @@ namespace Piwik\Plugins\CoreAdminHome;
use Exception;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
+use Piwik\ArchiveProcessor\Rules;
+use Piwik\Config;
use Piwik\Container\StaticContainer;
use Piwik\Archive\ArchiveInvalidator;
use Piwik\CronArchive;
@@ -20,6 +22,7 @@ use Piwik\Piwik;
use Piwik\Segment;
use Piwik\Scheduler\Scheduler;
use Piwik\Site;
+use Piwik\Url;
/**
* @method static \Piwik\Plugins\CoreAdminHome\API getInstance()
@@ -56,6 +59,58 @@ class API extends \Piwik\Plugin\API
}
/**
+ * @internal
+ */
+ public function setArchiveSettings($enableBrowserTriggerArchiving, $todayArchiveTimeToLive)
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ if (!Controller::isGeneralSettingsAdminEnabled()) {
+ throw new Exception('General settings admin is ont enabled');
+ }
+
+ Rules::setBrowserTriggerArchiving((bool)$enableBrowserTriggerArchiving);
+ Rules::setTodayArchiveTimeToLive($todayArchiveTimeToLive);
+
+ return true;
+ }
+
+ /**
+ * @internal
+ */
+ public function setTrustedHosts($trustedHosts)
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ if (!Controller::isGeneralSettingsAdminEnabled()) {
+ throw new Exception('General settings admin is ont enabled');
+ }
+
+ if (!empty($trustedHosts)) {
+ Url::saveTrustedHostnameInConfig($trustedHosts);
+ Config::getInstance()->forceSave();
+ }
+
+ return true;
+ }
+
+ /**
+ * @internal
+ */
+ public function setBrandingSettings($useCustomLogo)
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ $customLogo = new CustomLogo();
+ if ($useCustomLogo) {
+ $customLogo->enable();
+ } else {
+ $customLogo->disable();
+ }
+
+ return true;
+ }
+ /**
* Invalidates report data, forcing it to be recomputed during the next archiving run.
*
* Note: This is done automatically when tracking or importing visits in the past.
diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php
index 7c1d169ed6..5c2343083a 100644
--- a/plugins/CoreAdminHome/Controller.php
+++ b/plugins/CoreAdminHome/Controller.php
@@ -13,12 +13,11 @@ use Piwik\API\ResponseBuilder;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Common;
use Piwik\Config;
-use Piwik\Container\StaticContainer;
use Piwik\Menu\MenuTop;
-use Piwik\Nonce;
use Piwik\Piwik;
+use Piwik\Plugin;
use Piwik\Plugin\ControllerAdmin;
-use Piwik\Plugins\CorePluginsAdmin\UpdateCommunication;
+use Piwik\Plugins\CorePluginsAdmin\CorePluginsAdmin;
use Piwik\Plugins\CustomVariables\CustomVariables;
use Piwik\Plugins\LanguagesManager\LanguagesManager;
use Piwik\Plugins\PrivacyManager\DoNotTrackHeaderChecker;
@@ -28,6 +27,7 @@ use Piwik\Translation\Translator;
use Piwik\UpdateCheck;
use Piwik\Url;
use Piwik\View;
+use Piwik\Widget\WidgetsList;
class Controller extends ControllerAdmin
{
@@ -47,6 +47,23 @@ class Controller extends ControllerAdmin
parent::__construct();
}
+ public function home()
+ {
+ $isMarketplaceEnabled = CorePluginsAdmin::isMarketplaceEnabled();
+ $isFeedbackEnabled = Plugin\Manager::getInstance()->isPluginLoaded('Feedback');
+ $widgetsList = WidgetsList::get();
+
+ $hasDonateForm = $widgetsList->isDefined('CoreHome', 'getDonateForm');
+ $hasPiwikBlog = $widgetsList->isDefined('ExampleRssWidget', 'rssPiwik');
+
+ return $this->renderTemplate('home', array(
+ 'isMarketplaceEnabled' => $isMarketplaceEnabled,
+ 'isFeedbackEnabled' => $isFeedbackEnabled,
+ 'hasDonateForm' => $hasDonateForm,
+ 'hasPiwikBlog' => $hasPiwikBlog
+ ));
+ }
+
public function index()
{
$this->redirectToIndex('UsersManager', 'userSettings');
@@ -60,7 +77,7 @@ class Controller extends ControllerAdmin
$view = new View('@CoreAdminHome/generalSettings');
$this->handleGeneralSettingsAdmin($view);
- $view->trustedHosts = Url::getTrustedHostsFromConfig();
+ $view->trustedHosts = array_values(Url::getTrustedHostsFromConfig());
$logo = new CustomLogo();
$view->branding = array('use_custom_logo' => $logo->isEnabled());
$view->fileUploadEnabled = $logo->isFileUploadEnabled();
@@ -72,27 +89,49 @@ class Controller extends ControllerAdmin
$view->pathUserLogoSmall = CustomLogo::getPathUserLogoSmall();
$view->pathUserLogoSVG = CustomLogo::getPathUserSvgLogo();
$view->pathUserLogoDirectory = realpath(dirname($view->pathUserLogo) . '/');
+ $view->mailTypes = array(
+ '' => '',
+ 'Plain' => 'Plain',
+ 'Login' => 'Login',
+ 'Crammd5' => 'Crammd5',
+ );
+ $view->mailEncryptions = array(
+ '' => '',
+ 'ssl' => 'SSL',
+ 'tls' => 'TLS'
+ );
$view->language = LanguagesManager::getLanguageCodeForCurrentUser();
$this->setBasicVariablesView($view);
return $view->render();
}
- public function setGeneralSettings()
+ public function setMailSettings()
{
Piwik::checkUserHasSuperUserAccess();
- $response = new ResponseBuilder(Common::getRequestVar('format'));
+
+ if (!self::isGeneralSettingsAdminEnabled()) {
+ // General settings + Beta channel + SMTP settings is disabled
+ return '';
+ }
+
+ $response = new ResponseBuilder('json2');
try {
$this->checkTokenInUrl();
- $this->saveGeneralSettings();
+ // Update email settings
+ $mail = array();
+ $mail['transport'] = (Common::getRequestVar('mailUseSmtp') == '1') ? 'smtp' : '';
+ $mail['port'] = Common::getRequestVar('mailPort', '');
+ $mail['host'] = Common::unsanitizeInputValue(Common::getRequestVar('mailHost', ''));
+ $mail['type'] = Common::getRequestVar('mailType', '');
+ $mail['username'] = Common::unsanitizeInputValue(Common::getRequestVar('mailUsername', ''));
+ $mail['password'] = Common::unsanitizeInputValue(Common::getRequestVar('mailPassword', ''));
+ $mail['encryption'] = Common::getRequestVar('mailEncryption', '');
- $customLogo = new CustomLogo();
- if (Common::getRequestVar('useCustomLogo', '0')) {
- $customLogo->enable();
- } else {
- $customLogo->disable();
- }
+ Config::getInstance()->mail = $mail;
+
+ Config::getInstance()->forceSave();
$toReturn = $response->getResponse();
} catch (Exception $e) {
@@ -123,6 +162,8 @@ class Controller extends ControllerAdmin
$view->defaultSiteRevenue = Site::getCurrencySymbolFor($view->idSite);
$view->maxCustomVariables = CustomVariables::getNumUsableCustomVariables();
+ $view->defaultSite = array('id' => $view->idSite, 'name' => $view->defaultReportSiteName);
+
$allUrls = APISitesManager::getInstance()->getSiteUrlsFromId($view->idSite);
if (isset($allUrls[1])) {
$aliasUrl = $allUrls[1];
@@ -134,9 +175,6 @@ class Controller extends ControllerAdmin
$mainUrl = Site::getMainUrlFor($view->idSite);
$view->defaultReportSiteDomain = @parse_url($mainUrl, PHP_URL_HOST);
- // get currencies for each viewable site
- $view->currencySymbols = APISitesManager::getInstance()->getCurrencySymbols();
-
$dntChecker = new DoNotTrackHeaderChecker();
$view->serverSideDoNotTrackEnabled = $dntChecker->isActive();
@@ -171,61 +209,6 @@ class Controller extends ControllerAdmin
return (bool) Config::getInstance()->General['enable_general_settings_admin'];
}
- private function makeReleaseChannels()
- {
- return StaticContainer::get('Piwik\Plugin\ReleaseChannels');
- }
-
- private function saveGeneralSettings()
- {
- if (!self::isGeneralSettingsAdminEnabled()) {
- // General settings + Beta channel + SMTP settings is disabled
- return;
- }
-
- // General Setting
- $enableBrowserTriggerArchiving = Common::getRequestVar('enableBrowserTriggerArchiving');
- $todayArchiveTimeToLive = Common::getRequestVar('todayArchiveTimeToLive');
- Rules::setBrowserTriggerArchiving((bool)$enableBrowserTriggerArchiving);
- Rules::setTodayArchiveTimeToLive($todayArchiveTimeToLive);
-
- $releaseChannels = $this->makeReleaseChannels();
-
- // update beta channel setting
- $releaseChannel = Common::getRequestVar('releaseChannel', '', 'string');
- if (!$releaseChannels->isValidReleaseChannelId($releaseChannel)) {
- $releaseChannel = '';
- }
- $releaseChannels->setActiveReleaseChannelId($releaseChannel);
-
- // Update email settings
- $mail = array();
- $mail['transport'] = (Common::getRequestVar('mailUseSmtp') == '1') ? 'smtp' : '';
- $mail['port'] = Common::getRequestVar('mailPort', '');
- $mail['host'] = Common::unsanitizeInputValue(Common::getRequestVar('mailHost', ''));
- $mail['type'] = Common::getRequestVar('mailType', '');
- $mail['username'] = Common::unsanitizeInputValue(Common::getRequestVar('mailUsername', ''));
- $mail['password'] = Common::unsanitizeInputValue(Common::getRequestVar('mailPassword', ''));
- $mail['encryption'] = Common::getRequestVar('mailEncryption', '');
-
- Config::getInstance()->mail = $mail;
-
- // update trusted host settings
- $trustedHosts = Common::getRequestVar('trustedHosts', false, 'json');
- if ($trustedHosts !== false) {
- Url::saveTrustedHostnameInConfig($trustedHosts);
- }
-
- Config::getInstance()->forceSave();
-
- $pluginUpdateCommunication = new UpdateCommunication();
- if (Common::getRequestVar('enablePluginUpdateCommunication', '0', 'int')) {
- $pluginUpdateCommunication->enable();
- } else {
- $pluginUpdateCommunication->disable();
- }
- }
-
private function handleGeneralSettingsAdmin($view)
{
// Whether to display or not the general settings (cron, beta, smtp)
@@ -247,24 +230,7 @@ class Controller extends ControllerAdmin
$view->todayArchiveTimeToLiveDefault = Rules::getTodayArchiveTimeToLiveDefault();
$view->enableBrowserTriggerArchiving = $enableBrowserTriggerArchiving;
- $releaseChannels = $this->makeReleaseChannels();
- $activeChannelId = $releaseChannels->getActiveReleaseChannel()->getId();
- $allChannels = array();
- foreach ($releaseChannels->getAllReleaseChannels() as $channel) {
- $allChannels[] = array(
- 'id' => $channel->getId(),
- 'name' => $channel->getName(),
- 'description' => $channel->getDescription(),
- 'active' => $channel->getId() === $activeChannelId
- );
- }
-
- $view->releaseChannels = $allChannels;
$view->mail = Config::getInstance()->mail;
-
- $pluginUpdateCommunication = new UpdateCommunication();
- $view->canUpdateCommunication = $pluginUpdateCommunication->canBeEnabled();
- $view->enableSendPluginUpdateCommunication = $pluginUpdateCommunication->isEnabled();
}
}
diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php
index 6010391fdf..a87c6da948 100644
--- a/plugins/CoreAdminHome/CoreAdminHome.php
+++ b/plugins/CoreAdminHome/CoreAdminHome.php
@@ -55,10 +55,7 @@ class CoreAdminHome extends \Piwik\Plugin
$jsFiles[] = "libs/bower_components/sprintf/dist/sprintf.min.js";
$jsFiles[] = "plugins/Morpheus/javascripts/piwikHelper.js";
$jsFiles[] = "plugins/Morpheus/javascripts/ajaxHelper.js";
- $jsFiles[] = "plugins/Morpheus/javascripts/jquery.icheck.min.js";
- $jsFiles[] = "plugins/Morpheus/javascripts/morpheus.js";
$jsFiles[] = "plugins/CoreHome/javascripts/broadcast.js";
- $jsFiles[] = "plugins/CoreAdminHome/javascripts/generalSettings.js";
$jsFiles[] = "plugins/CoreHome/javascripts/donate.js";
$jsFiles[] = "plugins/CoreAdminHome/javascripts/protocolCheck.js";
}
@@ -83,5 +80,7 @@ class CoreAdminHome extends \Piwik\Plugin
{
$translationKeys[] = 'CoreAdminHome_ProtocolNotDetectedCorrectly';
$translationKeys[] = 'CoreAdminHome_ProtocolNotDetectedCorrectlySolution';
+ $translationKeys[] = 'CoreAdminHome_SettingsSaveSuccess';
+ $translationKeys[] = 'UserCountryMap_None';
}
}
diff --git a/plugins/CoreAdminHome/Menu.php b/plugins/CoreAdminHome/Menu.php
index 17d0d40910..ba3315ebe0 100644
--- a/plugins/CoreAdminHome/Menu.php
+++ b/plugins/CoreAdminHome/Menu.php
@@ -18,34 +18,29 @@ class Menu extends \Piwik\Plugin\Menu
{
public function configureAdminMenu(MenuAdmin $menu)
{
- $menu->addDevelopmentItem(null, array(), $order = 40);
- $menu->addManageItem(null, array(), $order = 1);
+ $menu->addPersonalItem(null, array(), 1, false);
+ $menu->addSystemItem(null, array(), 2, false);
+ $menu->addMeasurableItem(null, array(), $order = 3);
+ $menu->addPlatformItem(null, array(), 4, false);
$menu->addDiagnosticItem(null, array(), $order = 5);
+ $menu->addDevelopmentItem(null, array(), $order = 40);
if (Piwik::hasUserSuperUserAccess()) {
- $menu->addManageItem('General_GeneralSettings',
+ $menu->addSystemItem('General_GeneralSettings',
$this->urlForAction('generalSettings'),
$order = 5);
}
if (!Piwik::isUserIsAnonymous()) {
- $menu->addManageItem('CoreAdminHome_TrackingCode',
+ $menu->addMeasurableItem('CoreAdminHome_TrackingCode',
$this->urlForAction('trackingCodeGenerator'),
- $order = 11);
+ $order = 12);
}
}
public function configureTopMenu(MenuTop $menu)
{
- if (Piwik::isUserIsAnonymous()) {
- if (Plugin\Manager::getInstance()->isPluginActivated('ScheduledReports')) {
- $url = $this->urlForModuleAction('ScheduledReports', 'index');
- } else {
- $url = $this->urlForModuleAction('API', 'listAllAPI');
- }
- } else {
- $url = $this->urlForModuleAction('UsersManager', 'userSettings');
- }
+ $url = $this->urlForModuleAction('CoreAdminHome', 'home');
$menu->registerMenuIcon('CoreAdminHome_Administration', 'icon-configure');
$menu->addItem('CoreAdminHome_Administration', null, $url, 980, Piwik::translate('CoreAdminHome_Administration'));
diff --git a/plugins/CoreAdminHome/angularjs/archiving/archiving.controller.js b/plugins/CoreAdminHome/angularjs/archiving/archiving.controller.js
new file mode 100644
index 0000000000..c40685a300
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/archiving/archiving.controller.js
@@ -0,0 +1,45 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Controller to save archiving settings
+ */
+(function () {
+ angular.module('piwikApp').controller('ArchivingController', ArchivingController);
+
+ ArchivingController.$inject = ['$scope', 'piwikApi'];
+
+ function ArchivingController($scope, piwikApi) {
+
+ var self = this;
+ this.isLoading = false;
+
+ this.save = function () {
+
+ this.isLoading = true;
+
+ var enableBrowserTriggerArchiving = $('input[name=enableBrowserTriggerArchiving]:checked').val();
+ var todayArchiveTimeToLive = $('#todayArchiveTimeToLive').val();
+
+ piwikApi.post({module: 'API', method: 'CoreAdminHome.setArchiveSettings'}, {
+ enableBrowserTriggerArchiving: enableBrowserTriggerArchiving,
+ todayArchiveTimeToLive: todayArchiveTimeToLive
+ }).then(function (success) {
+ self.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
+ id: 'generalSettings', context: 'success'
+ });
+ notification.scrollToNotification();
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/branding/branding.controller.js b/plugins/CoreAdminHome/angularjs/branding/branding.controller.js
new file mode 100644
index 0000000000..97377941c0
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/branding/branding.controller.js
@@ -0,0 +1,108 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Controller to save mail smtp settings
+ */
+(function () {
+ angular.module('piwikApp').controller('BrandingController', BrandingController);
+
+ BrandingController.$inject = ['$scope', 'piwikApi'];
+
+ function BrandingController($scope, piwikApi) {
+
+ var self = this;
+ this.isLoading = false;
+
+ function refreshCustomLogo() {
+ var selectors = ['#currentLogo', '#currentFavicon'];
+ var index;
+ for (index = 0; index < selectors.length; index++) {
+ var imageDiv = $(selectors[index]);
+ if (imageDiv && imageDiv.data("src") && imageDiv.data("srcExists")) {
+ var logoUrl = imageDiv.data("src");
+ imageDiv.attr("src", logoUrl + "?" + (new Date()).getTime());
+ imageDiv.show();
+ } else {
+ imageDiv.hide();
+ }
+ }
+ }
+
+ this.updateLogo = function () {
+ var isSubmittingLogo = (this.customLogo != undefined && this.customLogo != '');
+ var isSubmittingFavicon = (this.customFavicon != undefined && this.customFavicon != '');
+
+ if (!isSubmittingLogo && !isSubmittingFavicon) {
+ return;
+ }
+
+ var $uploadError = $('.uploaderror');
+ $uploadError.fadeOut();
+ var frameName = "upload" + (new Date()).getTime();
+ var uploadFrame = $("<iframe name=\"" + frameName + "\" />");
+ uploadFrame.css("display", "none");
+ uploadFrame.load(function (data) {
+ setTimeout(function () {
+ var frameContent = $(uploadFrame.contents()).find('body').html();
+ frameContent = $.trim(frameContent);
+
+ if ('0' === frameContent) {
+ $uploadError.show();
+ } else {
+ // Upload succeed, so we update the images availability
+ // according to what have been uploaded
+ if (isSubmittingLogo) {
+ $('#currentLogo').data("srcExists", true)
+ }
+ if (isSubmittingFavicon) {
+ $('#currentFavicon').data("srcExists", true)
+ }
+ refreshCustomLogo();
+ }
+
+ if ('1' === frameContent || '0' === frameContent) {
+ uploadFrame.remove();
+ }
+ }, 1000);
+ });
+ $("body:first").append(uploadFrame);
+ var submittingForm = $('#logoUploadForm');
+ submittingForm.attr("target", frameName);
+ submittingForm.submit();
+
+ this.customLogo = '';
+ this.customFavicon = '';
+ };
+
+ refreshCustomLogo();
+
+ this.toggleCustomLogo = function () {
+ refreshCustomLogo();
+ };
+
+ this.save = function () {
+
+ this.isLoading = true;
+
+ piwikApi.post({module: 'API', method: 'CoreAdminHome.setBrandingSettings'}, {
+ useCustomLogo: this.enabled ? '1' : '0'
+ }).then(function (success) {
+ self.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
+ id: 'generalSettings', context: 'success'
+ });
+ notification.scrollToNotification();
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/smtp/mail-smtp.controller.js b/plugins/CoreAdminHome/angularjs/smtp/mail-smtp.controller.js
new file mode 100644
index 0000000000..1ef0ce0510
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/smtp/mail-smtp.controller.js
@@ -0,0 +1,50 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Controller to save mail smtp settings
+ */
+(function () {
+ angular.module('piwikApp').controller('MailSmtpController', MailSmtpController);
+
+ MailSmtpController.$inject = ['$scope', 'piwikApi'];
+
+ function MailSmtpController($scope, piwikApi) {
+
+ var self = this;
+ this.isLoading = false;
+
+ this.save = function () {
+
+ this.isLoading = true;
+
+ piwikApi.withTokenInUrl();
+ piwikApi.post({module: 'CoreAdminHome', action: 'setMailSettings'}, {
+ mailUseSmtp: this.enabled ? '1' : '0',
+ mailPort: this.mailPort,
+ mailHost: this.mailHost,
+ mailType: this.mailType,
+ mailUsername: this.mailUsername,
+ mailPassword: this.mailPassword,
+ mailEncryption: this.mailEncryption,
+ }).then(function (success) {
+
+ self.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
+ id: 'generalSettings', context: 'success'
+ });
+ notification.scrollToNotification();
+
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/trackingcode/imagetrackingcode.controller.js b/plugins/CoreAdminHome/angularjs/trackingcode/imagetrackingcode.controller.js
new file mode 100644
index 0000000000..18c97bab38
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trackingcode/imagetrackingcode.controller.js
@@ -0,0 +1,129 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Controller for image tracking code generator
+ */
+(function () {
+
+ // cache for not refetching data for same site twice
+ var sitePromises = {}, goalPromises = {};
+
+ angular.module('piwikApp').controller('ImageTrackingCodeController', ImageTrackingCodeController);
+
+ ImageTrackingCodeController.$inject = ['piwikApi', '$q'];
+
+ function ImageTrackingCodeController(piwikApi, $q) {
+
+ this.allGoals = {};
+ this.isLoading = false;
+
+ var piwikHost = window.location.host,
+ piwikPath = location.pathname.substring(0, location.pathname.lastIndexOf('/')),
+ self = this;
+
+ var currencyPromise = piwikApi.fetch({method: 'SitesManager.getCurrencySymbols', filter_limit: '-1'});
+
+ function requestSiteData(idSite)
+ {
+ if (!sitePromises[idSite]) {
+ sitePromises[idSite] = piwikApi.fetch({
+ module: 'API',
+ method: 'SitesManager.getSiteFromId',
+ idSite: idSite
+ });
+ }
+
+ return sitePromises[idSite];
+ }
+
+ function requestGoalData(idSite)
+ {
+ if (!goalPromises[idSite]) {
+ goalPromises[idSite] = piwikApi.fetch({
+ module: 'API',
+ method: 'Goals.getGoals',
+ idSite: idSite
+ });
+ }
+
+ return goalPromises[idSite];
+ }
+
+ // function that generates image tracker link
+ var generateImageTrackingAjax = null,
+ generateImageTrackerLink = function (trackingCodeChangedManually) {
+ // get data used to generate the link
+ var postParams = {
+ piwikUrl: piwikHost + piwikPath,
+ actionName: self.pageName
+ };
+
+ if (self.trackGoal && self.trackIdGoal) {
+ postParams.idGoal = self.trackIdGoal;
+ postParams.revenue = self.revenue;
+ }
+
+ if (generateImageTrackingAjax) {
+ generateImageTrackingAjax.abort();
+ }
+
+ generateImageTrackingAjax = piwikApi.post({
+ module: 'API',
+ format: 'json',
+ method: 'SitesManager.getImageTrackingCode',
+ idSite: self.site.id
+ }, postParams).then(function (response) {
+ generateImageTrackingAjax = null;
+
+ self.trackingCode = response.value;
+
+ if (trackingCodeChangedManually) {
+ var jsCodeTextarea = $('#image-tracking-text .codeblock');
+ jsCodeTextarea.effect("highlight", {}, 1500);
+ }
+ });
+ };
+
+ this.updateTrackingCode = function () {
+ generateImageTrackerLink(true);
+ };
+
+ this.changeSite = function (changedManually) {
+
+ self.isLoading = true;
+
+ var sitePromise = requestSiteData(this.site.id);
+ var goalPromise = requestGoalData(this.site.id);
+
+ return $q.all([currencyPromise, sitePromise, goalPromise]).then(function (data) {
+
+ self.isLoading = false;
+
+ var currencySymbols = data[0] || {};
+ var currency = data[1].currency || '';
+ var goals = data[2] || [];
+
+ var goalsList = [{key: '', value: _pk_translate('UserCountryMap_None')}];
+ for (var key in goals) {
+ goalsList.push({key: goals[key].idgoal, value: goals[key].name});
+ }
+
+ self.allGoals = goalsList;
+
+ $('[name=image-revenue] .site-currency').text(currencySymbols[currency.toUpperCase()]);
+ generateImageTrackerLink(changedManually);
+
+ });
+ };
+
+ if (this.site && this.site.id) {
+ this.changeSite(false);
+ }
+
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/trackingcode/jstrackingcode.controller.js b/plugins/CoreAdminHome/angularjs/trackingcode/jstrackingcode.controller.js
new file mode 100644
index 0000000000..4f222016c8
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trackingcode/jstrackingcode.controller.js
@@ -0,0 +1,151 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Controller for javascript tracking code generator
+ */
+(function () {
+
+ // gets the list of custom variables entered by the user in a custom variable section
+ function getCustomVariables(customVars) {
+ var result = [];
+ angular.forEach(customVars, function (customVar) {
+ result.push([customVar.name, customVar.value]);
+ });
+ return result;
+ };
+
+ // quickly gets the host + port from a url
+ function getHostNameFromUrl(url) {
+ var element = $('<a></a>')[0];
+ element.href = url;
+ return element.hostname;
+ };
+
+ angular.module('piwikApp').controller('JsTrackingCodeController', JsTrackingCodeController);
+
+ JsTrackingCodeController.$inject = ['$scope', 'piwikApi'];
+
+ function JsTrackingCodeController($scope, piwikApi) {
+
+ this.showAdvanced = false;
+ this.isLoading = false;
+ this.customVars = [];
+ this.siteUrls = {};
+ this.maxCustomVariables = parseInt(angular.element('[name=numMaxCustomVariables]').val(), 10);
+ this.canAddMoreCustomVariables = this.maxCustomVariables && this.maxCustomVariables > 0;
+
+ // get preloaded server-side data necessary for code generation
+ var piwikHost = window.location.host,
+ piwikPath = location.pathname.substring(0, location.pathname.lastIndexOf('/')),
+ self = this;
+
+ // 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 (self.siteUrls[idSite]) {
+ callback();
+ return;
+ }
+
+ // disable section
+ self.isLoading = true;
+
+ piwikApi.fetch({
+ module: 'API',
+ method: 'SitesManager.getSiteUrlsFromId',
+ idSite: idSite
+ }).then(function (data) {
+ self.siteUrls[idSite] = data || [];
+
+ // re-enable controls
+ self.isLoading = false;
+
+ callback();
+ });
+ };
+
+
+ // function that generates JS code
+ var generateJsCodeAjax = null;
+ var generateJsCode = function (trackingCodeChangedManually) {
+ // get params used to generate JS code
+ var params = {
+ piwikUrl: piwikHost + piwikPath,
+ groupPageTitlesByDomain: self.groupByDomain ? 1 : 0,
+ mergeSubdomains: self.trackAllSubdomains ? 1 : 0,
+ mergeAliasUrls: self.trackAllAliases ? 1 : 0,
+ visitorCustomVariables: getCustomVariables(self.customVars),
+ customCampaignNameQueryParam: null,
+ customCampaignKeywordParam: null,
+ doNotTrack: self.doNotTrack ? 1 : 0,
+ disableCookies: self.disableCookies ? 1 : 0
+ };
+
+ if (self.useCustomCampaignParams) {
+ params.customCampaignNameQueryParam = self.customCampaignName;
+ params.customCampaignKeywordParam = self.customCampaignKeyword;
+ }
+
+ if (generateJsCodeAjax) {
+ generateJsCodeAjax.abort();
+ }
+
+ generateJsCodeAjax = piwikApi.post({
+ module: 'API',
+ format: 'json',
+ method: 'SitesManager.getJavascriptTag',
+ idSite: self.site.id
+ }, params).then(function (response) {
+ generateJsCodeAjax = null;
+
+ self.trackingCode = response.value;
+
+ if(trackingCodeChangedManually) {
+ var jsCodeTextarea = $('#javascript-text .codeblock');
+ jsCodeTextarea.effect("highlight", {}, 1500);
+ }
+ });
+
+ return generateJsCodeAjax;
+ };
+
+ this.addCustomVar = function () {
+ if (this.canAddMoreCustomVariables) {
+ this.customVars.push({name: '', value: ''});
+ }
+
+ this.canAddMoreCustomVariables = this.maxCustomVariables > this.customVars.length;
+ };
+
+ this.addCustomVar();
+
+ this.updateTrackingCode = function () {
+ generateJsCode(true);
+ };
+
+ this.changeSite = function (trackingCodeChangedManually) {
+
+ $('.current-site-name').html(self.site.name);
+
+ getSiteData(this.site.id, '#js-code-options', function () {
+
+ var siteHost = getHostNameFromUrl(self.siteUrls[self.site.id][0]);
+ $('.current-site-host').text(siteHost);
+
+ var defaultAliasUrl = 'x.' + siteHost;
+ $('.current-site-alias').text(self.siteUrls[self.site.id][1] || defaultAliasUrl);
+
+ generateJsCode(true);
+ });
+ };
+
+ if (this.site && this.site.id) {
+ this.changeSite(false);
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.controller.js b/plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.controller.js
new file mode 100644
index 0000000000..adcaf69e29
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.controller.js
@@ -0,0 +1,61 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Controller to save archiving settings
+ */
+(function () {
+ angular.module('piwikApp').controller('TrustedHostsController', TrustedHostsController);
+
+ TrustedHostsController.$inject = ['$scope', 'piwikApi', '$timeout'];
+
+ function TrustedHostsController($scope, piwikApi, $timeout) {
+
+ var self = this;
+ this.isLoading = false;
+
+ this.addTrustedHost = function () {
+ this.hosts.push({host: ''});
+
+ $timeout(function () {
+ $('#trustedHostSettings').find('li:last input').val('').focus();
+ });
+ };
+
+ this.removeTrustedHost = function (index) {
+ this.hosts.splice(index,1);
+ };
+
+ this.save = function () {
+ var hosts = [];
+ angular.forEach(self.hosts, function (host) {
+ hosts.push(host.host);
+ });
+
+ var doSubmit = function () {
+ self.isLoading = true;
+
+ piwikApi.post({module: 'API', method: 'CoreAdminHome.setTrustedHosts'}, {
+ trustedHosts: hosts
+ }).then(function (success) {
+ self.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
+ id: 'generalSettings', context: 'success'
+ });
+ notification.scrollToNotification();
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+
+ piwikHelper.modalConfirm('#confirmTrustedHostChange', {yes: doSubmit});
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.directive.js b/plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.directive.js
new file mode 100644
index 0000000000..10485b8a1d
--- /dev/null
+++ b/plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.directive.js
@@ -0,0 +1,36 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-trusted-hosts-setting>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikTrustedHostsSetting', piwikTrustedHostsSetting);
+
+ piwikTrustedHostsSetting.$inject = ['piwik'];
+
+ function piwikTrustedHostsSetting(piwik){
+
+ return {
+ restrict: 'A',
+ transclude: true,
+ template: '<div ng-transclude></div>',
+ controller: 'TrustedHostsController',
+ controllerAs: 'trustedHosts',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, controller) {
+ controller.hosts = [];
+ angular.forEach(JSON.parse(attrs.piwikTrustedHostsSetting), function (host) {
+ controller.hosts.push({host: host});
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/javascripts/generalSettings.js b/plugins/CoreAdminHome/javascripts/generalSettings.js
deleted file mode 100644
index 3d4ab162fa..0000000000
--- a/plugins/CoreAdminHome/javascripts/generalSettings.js
+++ /dev/null
@@ -1,176 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-function sendGeneralSettingsAJAX() {
- var enableBrowserTriggerArchiving = $('input[name=enableBrowserTriggerArchiving]:checked').val();
- var enablePluginUpdateCommunication = $('input[name=enablePluginUpdateCommunication]:checked').val();
- var releaseChannel = $('input[name=releaseChannel]:checked').val();
- var todayArchiveTimeToLive = $('#todayArchiveTimeToLive').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,
- enablePluginUpdateCommunication: enablePluginUpdateCommunication,
- releaseChannel: releaseChannel,
- 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',
- action: 'setGeneralSettings'
- }, 'GET');
- ajaxHandler.withTokenInUrl();
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.send(true);
-}
-function showSmtpSettings(value) {
- $('#smtpSettings').toggle(value == 1);
-}
-function isSmtpEnabled() {
- return $('input[name="mailUseSmtp"]:checked').val();
-}
-function showCustomLogoSettings(value) {
- if (value == 1) {
- // Refresh custom logo only if we're going to display it
- refreshCustomLogo();
- }
- $('#logoSettings').toggle(value == 1);
-}
-function isCustomLogoEnabled() {
- return $('input[name="useCustomLogo"]:checked').val();
-}
-
-function refreshCustomLogo() {
- var selectors = ['#currentLogo', '#currentFavicon'];
- var index;
- for (index = 0; index < selectors.length; index++) {
- var imageDiv = $(selectors[index]);
- if (imageDiv && imageDiv.data("src") && imageDiv.data("srcExists")) {
- var logoUrl = imageDiv.data("src");
- 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();
- }
- });
-
- $('input[name=mailUseSmtp]').click(function () {
- showSmtpSettings($(this).val());
- });
- $('input[name=useCustomLogo]').click(function () {
- 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 isSubmittingLogo = ($('#customLogo').val() != '')
- var isSubmittingFavicon = ($('#customFavicon').val() != '')
- $('.uploaderror').fadeOut();
- var frameName = "upload" + (new Date()).getTime();
- var uploadFrame = $("<iframe name=\"" + frameName + "\" />");
- uploadFrame.css("display", "none");
- uploadFrame.load(function (data) {
- setTimeout(function () {
- var frameContent = $(uploadFrame.contents()).find('body').html();
- frameContent = $.trim(frameContent);
-
- if ('0' === frameContent) {
- $('.uploaderror').show();
- }
- else {
- // Upload succeed, so we update the images availability
- // according to what have been uploaded
- if (isSubmittingLogo) {
- $('#currentLogo').data("srcExists", true)
- }
- if (isSubmittingFavicon) {
- $('#currentFavicon').data("srcExists", true)
- }
- refreshCustomLogo();
- }
-
- if ('1' === frameContent || '0' === frameContent) {
- uploadFrame.remove();
- }
- }, 1000);
- });
- $("body:first").append(uploadFrame);
- submittingForm.attr("target", frameName);
- });
-
- $('#customLogo,#customFavicon').change(function () {
- $("#logoUploadForm").submit();
- $(this).val('');
- });
-
- // trusted hosts event handling
- var trustedHostSettings = $('#trustedHostSettings');
- trustedHostSettings.on('click', '.remove-trusted-host', function (e) {
- e.preventDefault();
- $(this).parent('li').remove();
- return false;
- });
- trustedHostSettings.find('.add-trusted-host').click(function (e) {
- e.preventDefault();
-
- // append new row to the table
- trustedHostSettings.find('ul').append(trustedHostSettings.find('li:last').clone());
- trustedHostSettings.find('li:last input').val('').focus();
- return false;
- });
-
-});
diff --git a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
deleted file mode 100644
index 3c983deca8..0000000000
--- a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
+++ /dev/null
@@ -1,320 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-(function ($, require) {
-
- var piwikHost = window.location.host,
- piwikPath = location.pathname.substring(0, location.pathname.lastIndexOf('/')),
- exports = require('piwik/Tracking');
-
- /**
- * This class is deprecated. Use server-side events instead.
- *
- * @deprecated
- */
- var TrackingCodeGenerator = function () {
- // empty
- };
-
- var TrackingCodeGeneratorSingleton = exports.TrackingCodeGenerator = new TrackingCodeGenerator();
-
- $(document).ready(function () {
-
- // get preloaded server-side data necessary for code generation
- var dataElement = $('#js-tracking-generator-data'),
- currencySymbols = JSON.parse(dataElement.attr('data-currencies')),
- maxCustomVariables = parseInt(dataElement.attr('max-custom-variables'), 10),
- siteUrls = {},
- siteCurrencies = {},
- allGoals = {},
- noneText = $('#image-tracker-goal').find('>option').text();
-
- //
- // 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;
- };
-
- // 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) {
- 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/>').val(goal.idgoal).text(goal.name));
- }
-
- // set currency string
- $('#' + id).parent().find('.currency').text(siteCurrencies[idsite]);
- };
-
- // function that generates JS code
- var generateJsCodeAjax = null,
- generateJsCode = function (trackingCodeChangedManually) {
- // get params used to generate JS code
- var params = {
- piwikUrl: piwikHost + piwikPath,
- groupPageTitlesByDomain: $('#javascript-tracking-group-by-domain').is(':checked') ? 1 : 0,
- mergeSubdomains: $('#javascript-tracking-all-subdomains').is(':checked') ? 1 : 0,
- mergeAliasUrls: $('#javascript-tracking-all-aliases').is(':checked') ? 1 : 0,
- visitorCustomVariables: getCustomVariables('javascript-tracking-visitor-cv'),
- pageCustomVariables: getCustomVariables('javascript-tracking-page-cv'),
- customCampaignNameQueryParam: null,
- customCampaignKeywordParam: null,
- doNotTrack: $('#javascript-tracking-do-not-track').is(':checked') ? 1 : 0,
- disableCookies: $('#javascript-tracking-disable-cookies').is(':checked') ? 1 : 0
- };
-
- if ($('#custom-campaign-query-params-check').is(':checked')) {
- params.customCampaignNameQueryParam = $('#custom-campaign-name-query-param').val();
- params.customCampaignKeywordParam = $('#custom-campaign-keyword-query-param').val();
- }
-
- if (generateJsCodeAjax) {
- generateJsCodeAjax.abort();
- }
-
- generateJsCodeAjax = new ajaxHelper();
- generateJsCodeAjax.addParams({
- module: 'API',
- format: 'json',
- method: 'SitesManager.getJavascriptTag',
- idSite: $('#js-tracker-website').attr('siteid')
- }, 'GET');
- generateJsCodeAjax.addParams(params, 'POST');
- generateJsCodeAjax.setCallback(function (response) {
- generateJsCodeAjax = null;
-
- var jsCodeTextarea = $('#javascript-text').find('textarea');
- jsCodeTextarea.val(response.value);
-
- if(trackingCodeChangedManually) {
- jsCodeTextarea.effect("highlight", {}, 1500);
- }
-
- });
- generateJsCodeAjax.send();
- };
-
- // function that generates image tracker link
- var generateImageTrackingAjax = null,
- generateImageTrackerLink = function (trackingCodeChangedManually) {
- // get data used to generate the link
- var generateDataParams = {
- piwikUrl: piwikHost + piwikPath,
- actionName: $('#image-tracker-action-name').val()
- };
-
- if ($('#image-tracking-goal-check').is(':checked')) {
- generateDataParams.idGoal = $('#image-tracker-goal').val();
- if (generateDataParams.idGoal) {
- generateDataParams.revenue = $('#image-goal-picker-extra').find('.revenue').val();
- }
- }
-
- if (generateImageTrackingAjax) {
- generateImageTrackingAjax.abort();
- }
-
- generateImageTrackingAjax = new ajaxHelper();
- generateImageTrackingAjax.addParams({
- module: 'API',
- format: 'json',
- method: 'SitesManager.getImageTrackingCode',
- idSite: $('#image-tracker-website').attr('siteid')
- }, 'GET');
- generateImageTrackingAjax.addParams(generateDataParams, 'POST');
- generateImageTrackingAjax.setCallback(function (response) {
- generateImageTrackingAjax = null;
-
- var jsCodeTextarea = $('#image-tracking-text').find('textarea');
- jsCodeTextarea.val(response.value);
-
- if(trackingCodeChangedManually) {
- jsCodeTextarea.effect("highlight", {}, 1500);
- }
- });
- generateImageTrackingAjax.send();
- };
-
- // on image link tracker site change, change available goals
- $('#image-tracker-website').bind('change', function (e, site) {
- getSiteData(site.id, '#image-tracking-code-options', function () {
- resetGoalSelectItems(site.id, 'image-tracker-goal');
- generateImageTrackerLink(true);
- });
- });
-
- // on js link tracker site change, change available goals
- $('#js-tracker-website').bind('change', function (e, site) {
- $('.current-site-name', '#optional-js-tracking-options').each(function () {
- $(this).html(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(true);
- });
- });
-
- // 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><input type="textbox" class="custom-variable-name"/></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
- // (X custom variables + 1 row for add new row)
- if ($('tr', row.parent()).length == (maxCustomVariables + 1)) {
- $(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(true);
- });
-
- // when any input/select in the image tracking options section changes, regenerate
- // image tracker link
- $('#image-tracking-section').on('change', 'input,select', function () {
- generateImageTrackerLink(true);
- });
-
- // on click generated code textareas, select the text so it can be easily copied
- $('#javascript-text>textarea,#image-tracking-text>textarea').click(function () {
- $(this).select();
- });
-
- // initial generation
- getSiteData(
- $('#js-tracker-website').attr('siteid'),
- '#js-code-options,#image-tracking-code-options',
- function () {
- var imageTrackerSiteId = $('#image-tracker-website').attr('siteid');
- resetGoalSelectItems(imageTrackerSiteId, 'image-tracker-goal');
-
- generateJsCode();
- generateImageTrackerLink();
- }
- );
- });
-
-}(jQuery, require));
diff --git a/plugins/CoreAdminHome/lang/en.json b/plugins/CoreAdminHome/lang/en.json
index 306fe77fd7..46003fe148 100644
--- a/plugins/CoreAdminHome/lang/en.json
+++ b/plugins/CoreAdminHome/lang/en.json
@@ -55,8 +55,9 @@
"LogoUploadHelp": "Please upload a file in %1$s formats with a minimum height of %2$s pixels.",
"MenuDiagnostic": "Diagnostic",
"MenuGeneralSettings": "General settings",
- "MenuManage": "Manage",
+ "MenuSystem": "System",
"MenuDevelopment": "Development",
+ "MenuMeasurables": "Measurables",
"OptOutComplete": "Opt-out complete; your visits to this website will not be recorded by the Web Analytics tool.",
"OptOutCompleteBis": "Note that if you clear your cookies, delete the opt-out cookie, or if you change computers or Web browsers, you will need to perform the opt-out procedure again.",
"OptOutDntFound": "You are not being tracked since your browser is reporting that you do not want to. This is a setting of your browser so you won't be able to opt-in until you disable the 'Do Not Track' feature.",
@@ -72,6 +73,7 @@
"PluginSettingsValueNotAllowed": "The value for field \"%1$s\" in plugin \"%2$s\" is not allowed",
"PluginSettingsSaveFailed": "Failed to save plugin settings",
"PluginSettingsSaveSuccess": "Plugin settings updated.",
+ "SettingsSaveSuccess": "Settings updated.",
"SendPluginUpdateCommunication": "Send an email when a plugin update is available",
"SendPluginUpdateCommunicationHelp": "An email will be sent to Super Users when there is a new version available for a plugin.",
"StableReleases": "If Piwik is a critical part of your business, we recommend you use the latest stable release. If you use the latest beta and you find a bug or have a suggestion, please %1$ssee here%2$s.",
diff --git a/plugins/CoreAdminHome/stylesheets/generalSettings.less b/plugins/CoreAdminHome/stylesheets/generalSettings.less
index dde1da48ed..958f51e468 100644
--- a/plugins/CoreAdminHome/stylesheets/generalSettings.less
+++ b/plugins/CoreAdminHome/stylesheets/generalSettings.less
@@ -14,6 +14,7 @@
#content.admin {
font-size: 13px;
+ margin-top: 0;
// Fix the <pre> blocks because of the display: table
pre {
@@ -26,141 +27,19 @@
margin-right: 8px;
}
-table.admin {
- font-size: 0.9em;
- background-color: @theme-color-background-base;
- border-collapse: collapse;
-}
-
-table.admin thead th {
- border-right: 1px solid @theme-color-background-base;
- color: @theme-color-background-base;
- 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;
-}
-
-table.admin tbody td {
- color: #414141;
- text-align: left;
- vertical-align: top;
-}
-
-table.admin tbody th {
- text-align: left;
- padding: 2px;
-}
-
-table.admin tbody td, table.admin tbody th {
- 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;
-}
-
.access_error {
font-size: .7em;
padding: 15px;
}
-.admin p + h2 {
- margin-top: 35px;
- // .admin p defines a margin-bottom of 10px, we make sure we still have a margin-top of 45px this way
-}
-
-.admin h2 {
- border-bottom: 0px;
- margin: 45px -15px 11px 0;
- padding: 0 0 5px 0;
- font-size: 24px;
- width: 100%;
-
- &:first-of-type:not(.secondary) {
- margin-top: 7px;
- margin-bottom: 16px;
- border-bottom: 1px solid @theme-color-background-tinyContrast;
- }
-}
-
-.admin h2 + h3 {
- margin-top: 0px;
-}
-
-.admin h3 {
- margin-top: 30px;
-}
-
.admin p, .admin section {
margin-top: 10px;
line-height: 140%;
padding-bottom: 10px;
}
-.adminTable {
- width: 100%;
- clear: both;
- margin: 0;
-}
-
-.adminTable a {
- text-decoration: none;
- color: #2B5C97;
-}
-
-.adminTable abbr {
- white-space: nowrap;
-}
-
-.adminTable td {
- font-size: 13px;
- vertical-align: top;
- padding: 7px 15px 9px 10px;
- vertical-align: top;
-}
-
-.adminTable td.action-links {
- text-align: right;
-}
-
-.adminTable .check-column {
- text-align: right;
- width: 1.5em;
- padding: 0;
-}
-
-.adminTable .num {
- text-align: center;
-}
-
-.adminTable .name {
- font-weight: bold;
-}
-
-.adminTable .ui-inline-help {
- margin-top: 0;
- width: 100%;
- margin-left: 50px;
-}
-
-.adminTable .columnHelp .ui-inline-help {
- margin-left: 0px;
-}
-
/* trusted host styles */
-#trustedHostSettings input {
+#trustedHostSettings input:not(.btn) {
width: 238px;
}
diff --git a/plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css b/plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css
index 2686567b3d..0c591087b0 100644
--- a/plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css
+++ b/plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css
@@ -1,20 +1,32 @@
-#javascript-output-section textarea, #image-link-output-section textarea {
- width: 100%;
- display: block;
- font-family: "Courier New", Courier, monospace;
-}
-#javascript-output-section textarea {
+#javascript-output-section .codeblock {
height: 312px;
}
-#image-link-output-section textarea {
+
+#image-link-output-section .codeblock {
height: 92px;
}
-#image-tracking-text {
- padding-top: 10px;
+.jsTrackingCodeWebsite .input-field,
+#image-tracking-code-options .input-field {
+ margin-top: 0;
+}
+
+#image-tracking-goal-sub .row,
+#javascript-tracking-visitor-cv .row,
+#js-campaign-query-param-extra .row {
+ margin-bottom: 0;
+}
+
+#javascript-tracking-visitor-cv > .row {
+ margin-left: 37px;
+}
+
+#image-tracking-goal-sub > .row,
+#js-campaign-query-param-extra > .row {
+ margin-left: 24px;
}
-#js-campaign-query-param-extra .form-group {
- /* terrible hack for a bug... */
- width: 600px;
+#image-tracking-goal-sub .input-field,
+#js-campaign-query-param-extra .input-field {
+ margin-top: 0;
}
diff --git a/plugins/CoreAdminHome/templates/generalSettings.twig b/plugins/CoreAdminHome/templates/generalSettings.twig
index 9dfb50ab07..b47bdb6db8 100644
--- a/plugins/CoreAdminHome/templates/generalSettings.twig
+++ b/plugins/CoreAdminHome/templates/generalSettings.twig
@@ -3,290 +3,276 @@
{% set title %}{{ 'CoreAdminHome_MenuGeneralSettings'|translate }}{% endset %}
{% block content %}
+
{% import 'macros.twig' as piwik %}
{% import 'ajaxMacros.twig' as ajax %}
{{ ajax.errorDiv() }}
{{ ajax.loadingDiv() }}
+<div piwik-content-block content-title="{{ 'CoreAdminHome_ArchivingSettings'|translate|e('html_attr') }}">
+ <div ng-controller="ArchivingController as archivingSettings">
+ {% if isGeneralSettingsAdminEnabled %}
+ <div class="form-group row">
+ <h3 class="col s12">{{ 'General_AllowPiwikArchivingToTriggerBrowser'|translate }}</h3>
+ <div class="col s12 m6">
+ <p>
+ <input type="radio" value="1" id="enableBrowserTriggerArchiving1"
+ name="enableBrowserTriggerArchiving" {% if enableBrowserTriggerArchiving==1 %} checked="checked"{% endif %}
+ />
+ <label for="enableBrowserTriggerArchiving1">
+ {{ 'General_Yes'|translate }}
+ <span class="form-description">{{ 'General_Default'|translate }}</span>
+ </label>
+ </p>
- <h2>{{ 'CoreAdminHome_ArchivingSettings'|translate }}</h2>
+ <p>
+ <input type="radio" value="0"
+ id="enableBrowserTriggerArchiving2"
+ name="enableBrowserTriggerArchiving"
+ {% if enableBrowserTriggerArchiving==0 %} checked="checked"{% endif %} />
- {% if isGeneralSettingsAdminEnabled %}
- <div class="form-group">
- <label>{{ 'General_AllowPiwikArchivingToTriggerBrowser'|translate }}</label>
- <div class="form-help">
- {{ 'General_ArchivingInlineHelp'|translate }}
- <br/>
- {{ 'General_SeeTheOfficialDocumentationForMoreInformation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>","</a>")|raw }}
+ <label for="enableBrowserTriggerArchiving2">
+ {{ 'General_No'|translate }}
+ <span class="form-description">{{ 'General_ArchivingTriggerDescription'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>","</a>")|raw }}</span>
+ </label>
+ </p>
+ </div><div class="col s12 m6">
+ <div class="form-help">
+ {{ 'General_ArchivingInlineHelp'|translate }}
+ <br/>
+ {{ 'General_SeeTheOfficialDocumentationForMoreInformation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>","</a>")|raw }}
+ </div>
+ </div>
</div>
- <label class="radio">
- <input type="radio" value="1" name="enableBrowserTriggerArchiving" {% if enableBrowserTriggerArchiving==1 %} checked="checked"{% endif %} />
- {{ 'General_Yes'|translate }}
- <span class="form-description">{{ 'General_Default'|translate }}</span>
- </label>
- <label class="radio">
- <input type="radio" value="0" name="enableBrowserTriggerArchiving" {% if enableBrowserTriggerArchiving==0 %} checked="checked"{% endif %} />
- {{ 'General_No'|translate }}
- <span class="form-description">{{ 'General_ArchivingTriggerDescription'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>","</a>")|raw }}</span>
- </label>
- </div>
- {% else %}
- <div class="form-group">
- <label>{{ 'General_AllowPiwikArchivingToTriggerBrowser'|translate }}</label>
- <label class="radio">
- <input type="radio" checked="checked" disabled="disabled" />
- {% if enableBrowserTriggerArchiving==1 %}
- {{ 'General_Yes'|translate }}
- {% else %}
- {{ 'General_No'|translate }}
- {% endif %}
- </label>
- </div>
- {% endif %}
+ {% else %}
+ <div piwik-field uicontrol="radio" name="mailUseSmtp"
+ disabled="true"
+ introduction="{{ 'General_AllowPiwikArchivingToTriggerBrowser'|translate|e('html_attr') }}"
+ title="{{ 'General_Yes'|translate|e('html_attr') }}"
+ value="1">
+ </div>
+ {% endif %}
- <div class="form-group">
- <label for="todayArchiveTimeToLive">
- {{ 'General_ReportsContainingTodayWillBeProcessedAtMostEvery'|translate }}
- </label>
- {% if isGeneralSettingsAdminEnabled %}
- <div class="form-help">
- {% if showWarningCron %}
- <strong>
- {{ 'General_NewReportsWillBeProcessedByCron'|translate }}<br/>
- {{ 'General_ReportsWillBeProcessedAtMostEveryHour'|translate }}
- {{ 'General_IfArchivingIsFastYouCanSetupCronRunMoreOften'|translate }}<br/>
- </strong>
+ <div class="form-group row">
+ <h3 class="col s12">
+ {{ 'General_ReportsContainingTodayWillBeProcessedAtMostEvery'|translate }}
+ </h3>
+ <div class="input-field col s12 m6">
+ <input type="text" value='{{ todayArchiveTimeToLive }}' id='todayArchiveTimeToLive' {% if not isGeneralSettingsAdminEnabled %}disabled="disabled"{% endif %} />
+ <span class="form-description">
+ {{ 'General_RearchiveTimeIntervalOnlyForTodayReports'|translate }}
+ </span>
+ </div>
+ <div class="col s12 m6">
+ {% if isGeneralSettingsAdminEnabled %}
+ <div class="form-help">
+ {% if showWarningCron %}
+ <strong>
+ {{ 'General_NewReportsWillBeProcessedByCron'|translate }}<br/>
+ {{ 'General_ReportsWillBeProcessedAtMostEveryHour'|translate }}
+ {{ 'General_IfArchivingIsFastYouCanSetupCronRunMoreOften'|translate }}<br/>
+ </strong>
+ {% endif %}
+ {{ 'General_SmallTrafficYouCanLeaveDefault'|translate( todayArchiveTimeToLiveDefault ) }}
+ <br/>
+ {{ 'General_MediumToHighTrafficItIsRecommendedTo'|translate(1800,3600) }}
+ </div>
{% endif %}
- {{ 'General_SmallTrafficYouCanLeaveDefault'|translate( todayArchiveTimeToLiveDefault ) }}
- <br/>
- {{ 'General_MediumToHighTrafficItIsRecommendedTo'|translate(1800,3600) }}
</div>
- {% endif %}
- <div class="input-group">
- <input value='{{ todayArchiveTimeToLive }}' id='todayArchiveTimeToLive' {% if not isGeneralSettingsAdminEnabled %}disabled="disabled"{% endif %} />
- <span class="input-group-addon">{{ 'Intl_NSeconds'|translate('') }}</span>
</div>
- <span class="form-description">
- {{ 'General_RearchiveTimeIntervalOnlyForTodayReports'|translate }}
- </span>
+
+ <div onconfirm="archivingSettings.save()" saving="archivingSettings.isLoading" piwik-save-button></div>
</div>
+</div>
+{% if isGeneralSettingsAdminEnabled %}
+ <div piwik-content-block content-title="{{ 'CoreAdminHome_EmailServerSettings'|translate|e('html_attr') }}">
- {% if isGeneralSettingsAdminEnabled %}
- <h2>{{ 'CoreAdminHome_UpdateSettings'|translate }}</h2>
-
- <div class="form-group">
- <label>{{ 'CoreAdminHome_ReleaseChannel'|translate }}</label>
- <div class="form-help">
- {{ 'CoreAdminHome_DevelopmentProcess'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/participate/development-process/' target='_blank'>","</a>")|raw }}
- <br/>
- {{ 'CoreAdminHome_StableReleases'|translate("<a href='?module=Proxy&action=redirect&url=http%3A%2F%2Fdeveloper.piwik.org%2Fguides%2Fcore-team-workflow%23influencing-piwik-development' target='_blank'>","</a>")|raw }}
- <br />
- {{ 'CoreAdminHome_LtsReleases'|translate }}
+ <div piwik-form ng-controller="MailSmtpController as mailSettings">
+ <div piwik-field uicontrol="checkbox" name="mailUseSmtp"
+ ng-model="mailSettings.enabled"
+ title="{{ 'General_UseSMTPServerForEmail'|translate|e('html_attr') }}"
+ value="{% if mail.transport == 'smtp' %}1{% endif %}"
+ inline-help="{{ 'General_SelectYesIfYouWantToSendEmailsViaServer'|translate|e('html_attr') }}">
</div>
- {% for releaseChannel in releaseChannels %}
- <label class="radio">
- <input type="radio" value="{{ releaseChannel.id|e('html_attr') }}" name="releaseChannel"{% if releaseChannel.active %} checked="checked"{% endif %} />
- {{ releaseChannel.name }}
- {% if releaseChannel.description %}
- <span class="form-description">{{ releaseChannel.description }}</span>
- {% endif %}
- </label>
- {% endfor %}
- </div>
- {% if canUpdateCommunication %}
- <div class="form-group">
- <label>{{ 'CoreAdminHome_SendPluginUpdateCommunication'|translate }}</label>
- <div class="form-help">
- {{ 'CoreAdminHome_SendPluginUpdateCommunicationHelp'|translate }}
+ <div id="smtpSettings"
+ ng-show="mailSettings.enabled">
+
+ <div piwik-field uicontrol="text" name="mailHost"
+ ng-model="mailSettings.mailHost"
+ title="{{ 'General_SmtpServerAddress'|translate|e('html_attr') }}"
+ value="{{ mail.host }}">
</div>
- <label class="radio">
- <input type="radio" name="enablePluginUpdateCommunication" value="1"
- {% if enableSendPluginUpdateCommunication==1 %} checked="checked"{% endif %}/>
- {{ 'General_Yes'|translate }}
- </label>
- <label class="radio">
- <input type="radio" name="enablePluginUpdateCommunication" value="0"
- {% if enableSendPluginUpdateCommunication==0 %} checked="checked"{% endif %}/>
- {{ 'General_No'|translate }}
- <span class="form-description">{{ 'General_Default'|translate }}</span>
- </label>
- </div>
- {% endif %}
- {% endif %}
- {% if isGeneralSettingsAdminEnabled %}
- <h2>{{ 'CoreAdminHome_EmailServerSettings'|translate }}</h2>
+ <div piwik-field uicontrol="text" name="mailPort"
+ ng-model="mailSettings.mailPort"
+ title="{{ 'General_SmtpPort'|translate|e('html_attr') }}"
+ value="{{ mail.port }}" inline-help="{{ 'General_OptionalSmtpPort'|translate|e('html_attr') }}">
+ </div>
- <div class="form-group">
- <label>{{ 'General_UseSMTPServerForEmail'|translate }}</label>
- <div class="form-help">
- {{ 'General_SelectYesIfYouWantToSendEmailsViaServer'|translate }}
- </div>
- <label class="radio">
- <input type="radio" name="mailUseSmtp" value="1" {% if mail.transport == 'smtp' %}checked{% endif %} />
- {{ 'General_Yes'|translate }}
- </label>
- <label class="radio">
- <input type="radio" name="mailUseSmtp" value="0" {% if mail.transport == '' %}checked{% endif %} />
- {{ 'General_No'|translate }}
- </label>
- </div>
+ <div piwik-field uicontrol="select" name="mailType"
+ ng-model="mailSettings.mailType"
+ title="{{ 'General_AuthenticationMethodSmtp'|translate|e('html_attr') }}"
+ options="{{ mailTypes|json_encode }}"
+ value="{{ mail.port }}" inline-help="{{ 'General_OnlyUsedIfUserPwdIsSet'|translate|e('html_attr') }}">
+ </div>
- <div id="smtpSettings">
- <div class="form-group">
- <label for="mailHost">{{ 'General_SmtpServerAddress'|translate }}</label>
- <input type="text" id="mailHost" value="{{ mail.host }}">
- </div>
- <div class="form-group">
- <label for="mailPort">{{ 'General_SmtpPort'|translate }}</label>
- <span class="form-help">{{ 'General_OptionalSmtpPort'|translate }}</span>
- <input type="text" id="mailPort" value="{{ mail.port }}">
- </div>
- <div class="form-group">
- <label for="mailType">{{ 'General_AuthenticationMethodSmtp'|translate }}</label>
- <span class="form-help">{{ 'General_OnlyUsedIfUserPwdIsSet'|translate }}</span>
- <select id="mailType">
- <option value="" {% if mail.type == '' %} selected="selected" {% endif %}></option>
- <option id="plain" {% if mail.type == 'Plain' %} selected="selected" {% endif %} value="Plain">Plain</option>
- <option id="login" {% if mail.type == 'Login' %} selected="selected" {% endif %} value="Login"> Login</option>
- <option id="cram-md5" {% if mail.type == 'Crammd5' %} selected="selected" {% endif %} value="Crammd5"> Crammd5</option>
- </select>
- </div>
- <div class="form-group">
- <label for="mailUsername">{{ 'General_SmtpUsername'|translate }}</label>
- <span class="form-help">{{ 'General_OnlyEnterIfRequired'|translate }}</span>
- <input type="text" id="mailUsername" value="{{ mail.username }}"/>
- </div>
- <div class="form-group">
- <label for="mailPassword">{{ 'General_SmtpPassword'|translate }}</label>
- <span class="form-help">
+ <div piwik-field uicontrol="text" name="mailUsername"
+ ng-model="mailSettings.mailUsername"
+ title="{{ 'General_SmtpUsername'|translate|e('html_attr') }}"
+ value="{{ mail.username }}" inline-help="{{ 'General_OnlyEnterIfRequired'|translate|e('html_attr') }}">
+ </div>
+
+ {% set help -%}
{{ 'General_OnlyEnterIfRequiredPassword'|translate }}<br/>
{{ 'General_WarningPasswordStored'|translate("<strong>","</strong>")|raw }}
- </span>
- <input type="password" id="mailPassword" value="{{ mail.password }}"/>
- </div>
- <div class="form-group">
- <label for="mailEncryption">{{ 'General_SmtpEncryption'|translate }}</label>
- <span class="form-help">{{ 'General_EncryptedSmtpTransport'|translate }}</span>
- <select id="mailEncryption">
- <option value="" {% if mail.encryption == '' %} selected="selected" {% endif %}></option>
- <option id="ssl" {% if mail.encryption == 'ssl' %} selected="selected" {% endif %} value="ssl">SSL</option>
- <option id="tls" {% if mail.encryption == 'tls' %} selected="selected" {% endif %} value="tls">TLS</option>
- </select>
+ {%- endset %}
+
+ <div piwik-field uicontrol="password" name="mailPassword"
+ ng-model="mailSettings.mailPassword"
+ title="{{ 'General_SmtpPassword'|translate|e('html_attr') }}"
+ value="{{ mail.password }}" inline-help="{{ help|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="select" name="mailEncryption"
+ ng-model="mailSettings.mailEncryption"
+ title="{{ 'General_SmtpEncryption'|translate|e('html_attr') }}"
+ options="{{ mailEncryptions|json_encode }}"
+ value="{{ mail.encryption }}" inline-help="{{ 'General_EncryptedSmtpTransport'|translate|e('html_attr') }}">
+ </div>
</div>
+
+ <div onconfirm="mailSettings.save()" saving="mailSettings.isLoading" piwik-save-button></div>
</div>
- {% endif %}
+ </div>
+{% endif %}
+<div piwik-content-block content-title="{{ 'CoreAdminHome_BrandingSettings'|translate|e('html_attr') }}">
- <h2>{{ 'CoreAdminHome_BrandingSettings'|translate }}</h2>
+ <div piwik-form ng-controller="BrandingController as brandingSettings">
- <p>{{ 'CoreAdminHome_CustomLogoHelpText'|translate }}</p>
+ <p>{{ 'CoreAdminHome_CustomLogoHelpText'|translate }}</p>
- <div class="form-group">
- <label>{{ 'CoreAdminHome_UseCustomLogo'|translate }}</label>
- <div class="form-help">
+ {% set help -%}
{% set giveUsFeedbackText %}"{{ 'General_GiveUsYourFeedback'|translate }}"{% endset %}
{{ 'CoreAdminHome_CustomLogoFeedbackInfo'|translate(giveUsFeedbackText,"<a href='?module=CorePluginsAdmin&action=plugins' rel='noreferrer' target='_blank'>","</a>")|raw }}
+ {%- endset %}
+
+ <div piwik-field uicontrol="checkbox" name="useCustomLogo"
+ ng-model="brandingSettings.enabled"
+ ng-change="brandingSettings.toggleCustomLogo()"
+ title="{{ 'CoreAdminHome_UseCustomLogo'|translate|e('html_attr') }}"
+ value="{% if branding.use_custom_logo == 1 %}1{% endif %}" inline-help="{{ help|e('html_attr') }}">
</div>
- <label class="radio">
- <input type="radio" name="useCustomLogo" value="1" {% if branding.use_custom_logo == 1 %}checked{% endif %} />
- {{ 'General_Yes'|translate }}
- </label>
- <label class="radio">
- <input type="radio" name="useCustomLogo" value="0" {% if branding.use_custom_logo == 0 %}checked{% endif %} />
- {{ 'General_No'|translate }}
- </label>
- </div>
- <div id="logoSettings">
- <form id="logoUploadForm" method="post" enctype="multipart/form-data" action="index.php?module=CoreAdminHome&format=json&action=uploadCustomLogo">
- {% if fileUploadEnabled %}
- <input type="hidden" name="token_auth" value="{{ token_auth }}"/>
+ <div id="logoSettings" ng-show="brandingSettings.enabled">
+ <form id="logoUploadForm" method="post" enctype="multipart/form-data" action="index.php?module=CoreAdminHome&format=json&action=uploadCustomLogo">
+ {% if fileUploadEnabled %}
+ <input type="hidden" name="token_auth" value="{{ token_auth }}"/>
- {% if logosWriteable %}
- <div class="alert alert-warning uploaderror" style="display:none;">
- {{ 'CoreAdminHome_LogoUploadFailed'|translate }}
- </div>
- <div class="form-group">
- <label for="customLogo">{{ 'CoreAdminHome_LogoUpload'|translate }}</label>
- <div class="form-help">{{ 'CoreAdminHome_LogoUploadHelp'|translate("JPG / PNG / GIF", 110) }}</div>
- <input name="customLogo" type="file" id="customLogo"/>
- <img data-src="{{ pathUserLogo }}" data-src-exists="{{ hasUserLogo ? '1':'0' }}" id="currentLogo" style="max-height: 150px"/>
- </div>
- <div class="form-group">
- <label for="customLogo">{{ 'CoreAdminHome_FaviconUpload'|translate }}</label>
- <div class="form-help">{{ 'CoreAdminHome_LogoUploadHelp'|translate("JPG / PNG / GIF", 16) }}</div>
- <input name="customFavicon" type="file" id="customFavicon"/>
- <img data-src="{{ pathUserFavicon }}" data-src-exists="{{ hasUserFavicon ? '1':'0' }}" id="currentFavicon" width="16" height="16"/>
- </div>
+ {% if logosWriteable %}
+ <div class="alert alert-warning uploaderror" style="display:none;">
+ {{ 'CoreAdminHome_LogoUploadFailed'|translate }}
+ </div>
+
+ <div piwik-field uicontrol="file" name="customLogo"
+ ng-change="brandingSettings.updateLogo()"
+ ng-model="brandingSettings.customLogo"
+ title="{{ 'CoreAdminHome_LogoUpload'|translate|e('html_attr') }}"
+ inline-help="{{ 'CoreAdminHome_LogoUploadHelp'|translate("JPG / PNG / GIF", 110)|e('html_attr') }}">
+ </div>
+
+ <div class="row">
+ <div class="col s12">
+ <img data-src="{{ pathUserLogo }}" data-src-exists="{{ hasUserLogo ? '1':'0' }}"
+ id="currentLogo" style="max-height: 150px"/>
+ </div>
+ </div>
+
+ <div piwik-field uicontrol="file" name="customFavicon"
+ ng-change="brandingSettings.updateLogo()"
+ ng-model="brandingSettings.customFavicon"
+ title="{{ 'CoreAdminHome_FaviconUpload'|translate|e('html_attr') }}"
+ inline-help="{{ 'CoreAdminHome_LogoUploadHelp'|translate("JPG / PNG / GIF", 16)|e('html_attr') }}">
+ </div>
+
+ <div class="row">
+ <div class="col s12">
+ <img data-src="{{ pathUserFavicon }}" data-src-exists="{{ hasUserFavicon ? '1':'0' }}"
+ id="currentFavicon" width="16" height="16"/>
+ </div>
+ </div>
+
+ {% else %}
+ <div class="alert alert-warning">
+ {{ 'CoreAdminHome_LogoNotWriteableInstruction'
+ |translate("<code>"~pathUserLogoDirectory~"</code><br/>", pathUserLogo ~", "~ pathUserLogoSmall ~", "~ pathUserLogoSVG ~"")|raw }}
+ </div>
+ {% endif %}
{% else %}
<div class="alert alert-warning">
- {{ 'CoreAdminHome_LogoNotWriteableInstruction'
- |translate("<code>"~pathUserLogoDirectory~"</code><br/>", pathUserLogo ~", "~ pathUserLogoSmall ~", "~ pathUserLogoSVG ~"")|raw }}
+ {{ 'CoreAdminHome_FileUploadDisabled'|translate("file_uploads=1") }}
</div>
{% endif %}
- {% else %}
- <div class="alert alert-warning">
- {{ 'CoreAdminHome_FileUploadDisabled'|translate("file_uploads=1") }}
- </div>
- {% endif %}
- </form>
+ </form>
+ </div>
+
+ <div onconfirm="brandingSettings.save()" saving="brandingSettings.isLoading" piwik-save-button></div>
</div>
+</div>
+<div piwik-content-block content-title="{{ 'CoreAdminHome_TrustedHostSettings'|translate|e('html_attr') }}">
+ <a name="trustedHostsSection"></a>
<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'>
+ <div id='trustedHostSettings' piwik-trusted-hosts-setting='{{ trustedHosts|json_encode }}'>
{% include "@CoreHome/_warningInvalidHost.twig" %}
{% if not isGeneralSettingsAdminEnabled %}
{{ 'CoreAdminHome_PiwikIsInstalledAt'|translate }}: {{ trustedHosts|join(", ") }}
{% else %}
- <div class="form-group">
+ <div class="form-group row">
<label>{{ 'CoreAdminHome_ValidPiwikHostname'|translate }}</label>
</div>
<ul>
- {% for hostIdx, host in trustedHosts %}
- <li>
- <input name="trusted_host" type="text" value="{{ host }}"/>
- <a href="#" class="remove-trusted-host btn btn-flat btn-lg" title="{{ 'General_Delete'|translate }}">
- <span class="icon-minus"></span>
- </a>
- </li>
- {% endfor %}
+ <li ng-repeat="trustedHost in trustedHosts.hosts">
+ <input ng-model="trustedHost.host" type="text"/>
+ <a href="javascript:;" ng-click="trustedHosts.removeTrustedHost($index);"
+ class="remove-trusted-host btn-flat btn-large" title="{{ 'General_Delete'|translate }}">
+ <span class="icon-minus"></span>
+ </a>
+ </li>
</ul>
<div class="add-trusted-host">
- <input type="text" placeholder="{{ 'CoreAdminHome_AddNewTrustedHost'|translate|e('html_attr') }}" readonly/>
+ <input type="text" ng-click="trustedHosts.addTrustedHost();"
+ placeholder="{{ 'CoreAdminHome_AddNewTrustedHost'|translate|e('html_attr') }}" readonly/>
- <a href="#" class="btn btn-flat btn-lg" title="{{ 'General_Add'|translate }}">
+ <a href="#" ng-click="trustedHosts.addTrustedHost();"
+ class="btn-flat btn-large" title="{{ 'General_Add'|translate }}">
<span class="icon-add"></span>
</a>
-
</div>
+
+ <div onconfirm="trustedHosts.save()" saving="trustedHosts.isLoading" piwik-save-button></div>
{% endif %}
</div>
- <input type="submit" value="{{ 'General_Save'|translate }}" class="submit generalSettingsSubmit"/>
- <br/><br/>
-
- {% if isDataPurgeSettingsEnabled %}
- <h2>{{ 'PrivacyManager_DeleteDataSettings'|translate }}</h2>
+</div>
+{% if isDataPurgeSettingsEnabled %}
+ <div piwik-content-block content-title="{{ 'PrivacyManager_DeleteDataSettings'|translate|e('html_attr') }}">
<p>{{ 'PrivacyManager_DeleteDataDescription'|translate }} {{ 'PrivacyManager_DeleteDataDescription2'|translate }}</p>
<p>
<a href='{{ linkTo({'module':"PrivacyManager", 'action':"privacySettings"}) }}#deleteLogsAnchor'>
{{ 'PrivacyManager_ClickHereSettings'|translate("'" ~ 'PrivacyManager_DeleteDataSettings'|translate ~ "'") }}
</a>
</p>
- {% endif %}
-
- <h2 piwik-enriched-headline>{{ 'CoreAdminHome_SystemPluginSettings'|translate }}</h2>
-
- <div piwik-plugin-settings mode="admin"></div>
+ </div>
+{% endif %}
+<div piwik-plugin-settings mode="admin"></div>
{% endblock %}
diff --git a/plugins/CoreAdminHome/templates/home.twig b/plugins/CoreAdminHome/templates/home.twig
new file mode 100644
index 0000000000..59d67bb190
--- /dev/null
+++ b/plugins/CoreAdminHome/templates/home.twig
@@ -0,0 +1,79 @@
+{% extends 'admin.twig' %}
+
+{% set title %}{{ 'CoreAdminHome_MenuGeneralSettings'|translate }}{% endset %}
+
+{% block content %}
+ {% set feedbackHelp %}
+ <div piwik-content-block content-title="Need help?">
+ <div>
+ There are different ways you can get help. There is free support via the Piwik Community and paid support
+ provided by the Piwik team and partners of Piwik. Or maybe do you have a bug to report or want to suggest a new
+ feature?
+ <br />
+ <br />
+ <a href="{{ linkTo({'module': 'Feedback', 'action': 'index'}) }}">Learn more</a>
+ </div>
+ </div>
+ {% endset %}
+
+ {% if isSuperUser %}
+ <div class="row">
+ <div class="col s12 {% if isFeedbackEnabled %}m4{% else %}m6{% endif %}">
+ <div piwik-content-block content-title="{{ 'CoreHome_SystemSummaryWidget'|translate }}">
+ <div piwik-widget-loader='{"module":"CoreHome","action":"getSystemSummary"}'></div>
+ </div>
+ </div>
+ <div class="col s12 {% if isFeedbackEnabled %}m4{% else %}m6{% endif %}">
+ <div piwik-content-block content-title="{{ 'Installation_SystemCheck'|translate }}">
+ <div piwik-widget-loader='{"module":"Installation","action":"getSystemCheck"}'></div>
+ </div>
+ </div>
+ {% if isFeedbackEnabled %}
+ <div class="col s12 m4">
+ {{ feedbackHelp|raw }}
+ </div>
+ {% endif %}
+ </div>
+ {% else %}
+ {{ feedbackHelp|raw }}
+ {% endif %}
+
+ {% if isMarketplaceEnabled %}
+ <div piwik-content-block content-title="Latest Marketplace Updates">
+ <div piwik-widget-loader='{"module":"CorePluginsAdmin","action":"getNewPlugins", "isAdminPage": "1"}'></div>
+ </div>
+ {% endif %}
+
+ {{ postEvent('Template.adminHome') }}
+
+ <style type="text/css">
+ #content .piwik-donate-call {
+ padding: 0;
+ border: 0;
+ max-width: none;
+ }
+ .theWidgetContent .rss {
+ margin: -10px -15px;
+ }
+ </style>
+
+ {% if hasDonateForm or hasPiwikBlog %}
+ <div class="row">
+ {% if hasDonateForm %}
+ <div class="col s12 {% if hasPiwikBlog %}m6{% endif %}">
+ <div piwik-content-block content-title="{{ 'CoreHome_SupportPiwik'|translate }}">
+ <div piwik-widget-loader='{"module":"CoreHome","action":"getDonateForm","widget": "0"}'></div>
+ </div>
+ </div>
+ {% endif %}
+ {% if hasPiwikBlog %}
+ <div class="col s12 {% if hasDonateForm %}m6{% endif %}">
+ <div piwik-content-block content-title="Piwik.org Blog">
+ <div piwik-widget-loader='{"module":"ExampleRssWidget","action":"rssPiwik"}'></div>
+ </div>
+ </div>
+ {% endif %}
+ </div>
+ {% endif %}
+
+{% endblock %}
diff --git a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig
index 535401dff3..5e0acb6169 100644
--- a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig
+++ b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig
@@ -3,254 +3,298 @@
{% block head %}
{{ parent() }}
<link rel="stylesheet" href="plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css" />
- <script type="text/javascript" src="plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js"></script>
{% endblock %}
-
{% set title %}{{ 'CoreAdminHome_JavaScriptTracking'|translate }}{% endset %}
{% block content %}
-<div id="js-tracking-generator-data" max-custom-variables="{{ maxCustomVariables|e('html_attr') }}" data-currencies="{{ currencySymbols|json_encode }}"></div>
-<h2 piwik-enriched-headline
- feature-name="{{ 'CoreAdminHome_TrackingCode'|translate }}"
- help-url="http://piwik.org/docs/tracking-api/">{{ title }}</h2>
+ <input type="hidden" name="numMaxCustomVariables"
+ value="{{ maxCustomVariables|e('html_attr') }}">
-<div id="js-code-options">
+<div piwik-content-block
+ content-title="{{ title|e('html_attr') }}"
+ help-url="http://piwik.org/docs/tracking-api/"
+ rate="{{ 'CoreAdminHome_TrackingCode'|translate|e('html_attr') }}">
- <p>
- {{ 'CoreAdminHome_JSTrackingIntro1'|translate }}
- <br/><br/>
- {{ 'CoreAdminHome_JSTrackingIntro2'|translate }} {{ 'CoreAdminHome_JSTrackingIntro3'|translate('<a href="http://piwik.org/integrate/" rel="noreferrer" target="_blank">','</a>')|raw }}
- <br/><br/>
- {{ 'CoreAdminHome_JSTrackingIntro4'|translate('<a href="#image-tracking-link">','</a>')|raw }}
- <br/><br/>
- {{ 'CoreAdminHome_JSTrackingIntro5'|translate('<a rel="noreferrer" target="_blank" href="http://piwik.org/docs/javascript-tracking/">','</a>')|raw }}
- </p>
+ <div id="js-code-options" ng-controller="JsTrackingCodeController as jsTrackingCode">
- <div class="form-group">
- {# website #}
- <label>{{ 'General_Website'|translate }}</label>
-
- <div piwik-siteselector
- class="sites_autocomplete"
- siteid="{{ idSite }}"
- sitename="{{ defaultReportSiteName }}"
- show-all-sites-item="false"
- switch-site-on-select="false"
- id="js-tracker-website"
- show-selected-site="true"></div>
- </div>
+ <p>
+ {{ 'CoreAdminHome_JSTrackingIntro1'|translate }}
+ <br/><br/>
+ {{ 'CoreAdminHome_JSTrackingIntro2'|translate }} {{ 'CoreAdminHome_JSTrackingIntro3'|translate('<a href="http://piwik.org/integrate/" rel="noreferrer" target="_blank">','</a>')|raw }}
+ <br/><br/>
+ {{ 'CoreAdminHome_JSTrackingIntro4'|translate('<a href="#image-tracking-link">','</a>')|raw }}
+ <br/><br/>
+ {{ 'CoreAdminHome_JSTrackingIntro5'|translate('<a rel="noreferrer" target="_blank" href="http://piwik.org/docs/javascript-tracking/">','</a>')|raw }}
+ </p>
- <h3>{{ 'General_Options'|translate }}</h3>
+ <div piwik-field uicontrol="site" name="js-tracker-website"
+ class="jsTrackingCodeWebsite"
+ ng-model="jsTrackingCode.site"
+ ng-change="jsTrackingCode.changeSite(true)"
+ introduction="{{ 'General_Website'|translate|e('html_attr') }}"
+ value='{{ defaultSite|json_encode }}'>
+ </div>
- <div id="optional-js-tracking-options">
+ <div id="optional-js-tracking-options">
- {# track across all subdomains #}
- <div class="form-group">
- <div class="form-help">
- {{ 'CoreAdminHome_JSTracking_MergeSubdomainsDesc'|translate("x.<span class='current-site-host'>"~defaultReportSiteDomain~"</span>","y.<span class='current-site-host'>"~defaultReportSiteDomain~"</span>")|raw }}
+ {# track across all subdomains #}
+ <div id="jsTrackAllSubdomainsInlineHelp" class="inline-help-node">
+ {{ 'CoreAdminHome_JSTracking_MergeSubdomainsDesc'|translate("x.<span class='current-site-host'></span>","y.<span class='current-site-host'></span>")|raw }}
{{ 'General_LearnMore'|translate(' (<a href="http://developer.piwik.org/guides/tracking-javascript-guide#measuring-domains-andor-sub-domains" rel="noreferrer" target="_blank">', '</a>)')|raw }}
</div>
- <label class="checkbox">
- <input type="checkbox" id="javascript-tracking-all-subdomains"/>
- {{ 'CoreAdminHome_JSTracking_MergeSubdomains'|translate }}
- <span class='current-site-name'>{{ defaultReportSiteName|raw }}</span>
- </label>
- </div>
- {# group page titles by site domain #}
- <div class="form-group">
- <div class="form-help">
- {{ 'CoreAdminHome_JSTracking_GroupPageTitlesByDomainDesc1'|translate("<span class='current-site-host'>" ~ defaultReportSiteDomain ~ "</span>")|raw }}
+ <div piwik-field uicontrol="checkbox" name="javascript-tracking-all-subdomains"
+ ng-model="jsTrackingCode.trackAllSubdomains"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ disabled="jsTrackingCode.isLoading"
+ introduction="{{ 'General_Options'|translate|e('html_attr') }}"
+ title="{{ 'CoreAdminHome_JSTracking_MergeSubdomains'|translate|e('html_attr') }} <span class='current-site-name'></span>"
+ value="" inline-help="#jsTrackAllSubdomainsInlineHelp">
</div>
- <label class="checkbox">
- <input type="checkbox" id="javascript-tracking-group-by-domain"/>
- {{ 'CoreAdminHome_JSTracking_GroupPageTitlesByDomain'|translate }}
- </label>
- </div>
- {# track across all site aliases #}
- <div class="form-group">
- <div class="form-help">
- {{ 'CoreAdminHome_JSTracking_MergeAliasesDesc'|translate("<span class='current-site-alias'>"~defaultReportSiteAlias~"</span>")|raw }}
+ {# group page titles by site domain #}
+ <div id="jsTrackGroupByDomainInlineHelp" class="inline-help-node">
+ {{ 'CoreAdminHome_JSTracking_GroupPageTitlesByDomainDesc1'|translate("<span class='current-site-host'></span>")|raw }}
+ </div>
+
+ <div piwik-field uicontrol="checkbox" name="javascript-tracking-group-by-domain"
+ ng-model="jsTrackingCode.groupByDomain"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ disabled="jsTrackingCode.isLoading"
+ title="{{ 'CoreAdminHome_JSTracking_GroupPageTitlesByDomain'|translate|e('html_attr') }}"
+ value="" inline-help="#jsTrackGroupByDomainInlineHelp">
</div>
- <label class="checkbox">
- <input type="checkbox" checked="checked" id="javascript-tracking-all-aliases"/>
- {{ 'CoreAdminHome_JSTracking_MergeAliases'|translate }}
- <span class='current-site-name'>{{ defaultReportSiteName|raw }}</span>
- </label>
- </div>
- <h3>{{ 'Mobile_Advanced'|translate }}</h3>
+ {# track across all site aliases #}
+ <div id="jsTrackAllAliasesInlineHelp" class="inline-help-node">
+ {{ 'CoreAdminHome_JSTracking_MergeAliasesDesc'|translate("<span class='current-site-alias'></span>")|raw }}
+ </div>
- <p>
- <a href="#" class="section-toggler-link" data-section-id="javascript-advanced-options">{{ 'General_Show'|translate }}</a>
- </p>
+ <div piwik-field uicontrol="checkbox" name="javascript-tracking-all-aliases"
+ ng-model="jsTrackingCode.trackAllAliases"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ disabled="jsTrackingCode.isLoading"
+ title="{{ 'CoreAdminHome_JSTracking_MergeAliases'|translate|e('html_attr') }} <span class='current-site-name'></span>"
+ value="" inline-help="#jsTrackAllAliasesInlineHelp">
+ </div>
- <div id="javascript-advanced-options" style="display:none;">
+ <h3>{{ 'Mobile_Advanced'|translate }}</h3>
+
+ <p>
+ <a href="javascript:;"
+ ng-show="!jsTrackingCode.showAdvanced"
+ ng-click="jsTrackingCode.showAdvanced = true">{{ 'General_Show'|translate }}</a>
+ <a href="javascript:;"
+ ng-show="jsTrackingCode.showAdvanced"
+ ng-click="jsTrackingCode.showAdvanced = false">{{ 'General_Hide'|translate }}</a>
+ </p>
+
+ <div id="javascript-advanced-options" ng-show="jsTrackingCode.showAdvanced">
+
+ {# visitor custom variable #}
+ <div piwik-field uicontrol="checkbox" name="javascript-tracking-visitor-cv-check"
+ class="section-toggler-link"
+ ng-model="jsTrackingCode.trackCustomVars"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ disabled="jsTrackingCode.isLoading"
+ title="{{ 'CoreAdminHome_JSTracking_VisitorCustomVars'|translate|e('html_attr') }}"
+ value="" inline-help="{{ 'CoreAdminHome_JSTracking_VisitorCustomVarsDesc'|translate|e('html_attr') }}">
+ </div>
- {# visitor custom variable #}
- <div id="javascript-tracking-visitor-cv">
- <div class="form-group">
- <div class="form-help">
- {{ 'CoreAdminHome_JSTracking_VisitorCustomVarsDesc'|translate }}
+ <div id="javascript-tracking-visitor-cv" ng-show="jsTrackingCode.trackCustomVars">
+ <div class="row">
+ <div class="col s12 m3">
+ {{ 'General_Name'|translate }}
+ </div>
+ <div class="col s12 m3">
+ {{ 'General_Value'|translate }}
+ </div>
+ </div>
+ <div class="row" ng-repeat="customVar in jsTrackingCode.customVars">
+ <div class="col s12 m6 l3">
+ <input type="text" class="custom-variable-name"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ ng-model="jsTrackingCode.customVars[$index.toString()].name"
+ placeholder="e.g. Type"/>
+ </div>
+ <div class="col s12 m6 l3">
+ <input type="text" class="custom-variable-value"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ ng-model="jsTrackingCode.customVars[$index.toString()].value"
+ placeholder="e.g. Customer"/>
+ </div>
+ </div>
+ <div class="row" ng-show="jsTrackingCode.canAddMoreCustomVariables">
+ <div class="col s12">
+ <a href="javascript:;"
+ ng-click="jsTrackingCode.addCustomVar()"
+ class="add-custom-variable"><span class="icon-add"></span> {{ 'General_Add'|translate }}</a>
+ </div>
</div>
- <label class="checkbox">
- <input class="section-toggler-link" type="checkbox" id="javascript-tracking-visitor-cv-check" data-section-id="js-visitor-cv-extra"/>
- {{ 'CoreAdminHome_JSTracking_VisitorCustomVars'|translate }}
- </label>
</div>
- <table id="js-visitor-cv-extra" style="display:none;">
- <tr>
- <th>{{ 'General_Name'|translate }}</th>
- <th>{{ 'General_Value'|translate }}</th>
- </tr>
- <tr>
- <td><input type="text" class="custom-variable-name" placeholder="e.g. Type"/></td>
- <td><input type="text" 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>
- {# do not track support #}
- <div class="form-group">
- <div class="form-help">
+ {# do not track support #}
+ <div id="jsDoNotTrackInlineHelp" class="inline-help-node">
{{ 'CoreAdminHome_JSTracking_EnableDoNotTrackDesc'|translate }}
{% if serverSideDoNotTrackEnabled %}
<br/>
{{ 'CoreAdminHome_JSTracking_EnableDoNotTrack_AlreadyEnabled'|translate }}
{% endif %}
</div>
- <label class="checkbox">
- <input type="checkbox" id="javascript-tracking-do-not-track"/>
- {{ 'CoreAdminHome_JSTracking_EnableDoNotTrack'|translate }}
- </label>
- </div>
- {# disable all cookies options #}
- <div class="form-group">
- <div class="form-help">
- {{ 'CoreAdminHome_JSTracking_DisableCookiesDesc'|translate }}
+ <div piwik-field uicontrol="checkbox" name="javascript-tracking-do-not-track"
+ ng-model="jsTrackingCode.doNotTrack"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ disabled="jsTrackingCode.isLoading"
+ title="{{ 'CoreAdminHome_JSTracking_EnableDoNotTrack'|translate|e('html_attr') }}"
+ value="" inline-help="#jsDoNotTrackInlineHelp">
+ </div>
+
+ {# disable all cookies options #}
+ <div piwik-field uicontrol="checkbox" name="javascript-tracking-disable-cookies"
+ ng-model="jsTrackingCode.disableCookies"
+ disabled="jsTrackingCode.isLoading"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ title="{{ 'CoreAdminHome_JSTracking_DisableCookies'|translate|e('html_attr') }}"
+ value="" inline-help="{{ 'CoreAdminHome_JSTracking_DisableCookiesDesc'|translate|e('html_attr') }}">
</div>
- <label class="checkbox">
- <input type="checkbox" id="javascript-tracking-disable-cookies"/>
- {{ 'CoreAdminHome_JSTracking_DisableCookies'|translate }}
- </label>
- </div>
- {# custom campaign name/keyword query params #}
- <div class="form-group">
- <div class="form-help">
+ {# custom campaign name/keyword query params #}
+ <div id="jsTrackCampaignParamsInlineHelp" class="inline-help-node">
{{ 'CoreAdminHome_JSTracking_CustomCampaignQueryParamDesc'|translate('<a href="http://piwik.org/faq/general/#faq_119" rel="noreferrer" target="_blank">','</a>')|raw }}
</div>
- <label class="checkbox">
- <input class="section-toggler-link" type="checkbox"
- id="custom-campaign-query-params-check"
- data-section-id="js-campaign-query-param-extra"/>
- {{ 'CoreAdminHome_JSTracking_CustomCampaignQueryParam'|translate }}
- </label>
- </div>
- <div style="display:none;" id="js-campaign-query-param-extra">
- <div class="form-group">
- <label>{{ 'CoreAdminHome_JSTracking_CampaignNameParam'|translate }}</label>
- <input type="text" id="custom-campaign-name-query-param"/>
+
+ <div piwik-field uicontrol="checkbox" name="custom-campaign-query-params-check"
+ ng-model="jsTrackingCode.useCustomCampaignParams"
+ disabled="jsTrackingCode.isLoading"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ title="{{ 'CoreAdminHome_JSTracking_CustomCampaignQueryParam'|translate|e('html_attr') }}"
+ value="" inline-help="#jsTrackCampaignParamsInlineHelp">
</div>
- <div class="form-group">
- <label>{{ 'CoreAdminHome_JSTracking_CampaignKwdParam'|translate }}</label>
- <input type="text" id="custom-campaign-keyword-query-param"/>
+
+ <div ng-show="jsTrackingCode.useCustomCampaignParams" id="js-campaign-query-param-extra">
+ <div class="row">
+ <div class="col s12">
+ <div piwik-field uicontrol="text" name="custom-campaign-name-query-param"
+ ng-model="jsTrackingCode.customCampaignName"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ disabled="jsTrackingCode.isLoading"
+ title="{{ 'CoreAdminHome_JSTracking_CampaignNameParam'|translate|e('html_attr') }}"
+ value="">
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col s12">
+ <div piwik-field uicontrol="text" name="custom-campaign-keyword-query-param"
+ ng-model="jsTrackingCode.customCampaignKeyword"
+ ng-change="jsTrackingCode.updateTrackingCode()"
+ disabled="jsTrackingCode.isLoading"
+ title="{{ 'CoreAdminHome_JSTracking_CampaignKwdParam'|translate|e('html_attr') }}"
+ value="">
+ </div>
+ </div>
+ </div>
</div>
+
</div>
</div>
- </div>
-
-</div>
-
-<div id="javascript-output-section">
- <h3>{{ 'General_JsTrackingTag'|translate }}</h3>
+ <div id="javascript-output-section">
+ <h3>{{ 'General_JsTrackingTag'|translate }}</h3>
- <p>{{ 'CoreAdminHome_JSTracking_CodeNoteBeforeClosingHead'|translate("&lt;/head&gt;")|raw }}</p>
+ <p>{{ 'CoreAdminHome_JSTracking_CodeNoteBeforeClosingHead'|translate("&lt;/head&gt;")|raw }}</p>
- <div id="javascript-text">
- <textarea class="codeblock"> </textarea>
+ <div id="javascript-text">
+ <pre piwik-select-on-focus class="codeblock"
+ ng-bind="jsTrackingCode.trackingCode"> </pre>
+ </div>
+ </div>
</div>
</div>
-<h2 id="image-tracking-link">{{ 'CoreAdminHome_ImageTracking'|translate }}</h2>
-
-<div id="image-tracking-code-options">
-
- <p>
- {{ 'CoreAdminHome_ImageTrackingIntro1'|translate }} {{ 'CoreAdminHome_ImageTrackingIntro2'|translate("<em>&lt;noscript&gt;&lt;/noscript&gt;</em>")|raw }}
- </p>
- <p>
- {{ 'CoreAdminHome_ImageTrackingIntro3'|translate('<a href="http://piwik.org/docs/tracking-api/reference/" rel="noreferrer" target="_blank">','</a>')|raw }}
- </p>
+<div piwik-content-block content-title="{{ 'CoreAdminHome_ImageTracking'|translate|e('html_attr') }}">
+ <a name="image-tracking-link"></a>
- {# website #}
- <div class="form-group">
- <label>{{ 'General_Website'|translate }}</label>
- <div piwik-siteselector
- class="sites_autocomplete"
- siteid="{{ idSite }}"
- sitename="{{ defaultReportSiteName }}"
- id="image-tracker-website"
- show-all-sites-item="false"
- switch-site-on-select="false"
- show-selected-site="true"></div>
- </div>
+ <div id="image-tracking-code-options" ng-controller="ImageTrackingCodeController as imageTrackingCode">
- <h3>{{ 'General_Options'|translate }}</h3>
+ <p>
+ {{ 'CoreAdminHome_ImageTrackingIntro1'|translate }} {{ 'CoreAdminHome_ImageTrackingIntro2'|translate("<code>&lt;noscript&gt;&lt;/noscript&gt;</code>")|raw }}
+ </p>
+ <p>
+ {{ 'CoreAdminHome_ImageTrackingIntro3'|translate('<a href="http://piwik.org/docs/tracking-api/reference/" rel="noreferrer" target="_blank">','</a>')|raw }}
+ </p>
- <div id="image-tracking-section">
+ {# website #}
+ <div piwik-field uicontrol="site" name="image-tracker-website"
+ ng-model="imageTrackingCode.site"
+ ng-change="imageTrackingCode.changeSite(true)"
+ introduction="{{ 'General_Website'|translate|e('html_attr') }}"
+ value='{{ defaultSite|json_encode }}'>
+ </div>
{# action_name #}
- <div class="form-group">
- <label for="image-tracker-action-name">{{ 'Actions_ColumnPageName'|translate }}</label>
- <input type="text" id="image-tracker-action-name"/>
+ <div piwik-field uicontrol="text" name="image-tracker-action-name"
+ ng-model="imageTrackingCode.pageName"
+ ng-change="imageTrackingCode.updateTrackingCode()"
+ disabled="imageTrackingCode.isLoading"
+ introduction="{{ 'General_Options'|translate|e('html_attr') }}"
+ title="{{ 'Actions_ColumnPageName'|translate|e('html_attr') }}"
+ value="">
</div>
{# goal #}
- <div class="form-group">
- <label class="checkbox">
- <input class="section-toggler-link" type="checkbox" id="image-tracking-goal-check" data-section-id="image-goal-picker-extra"/>
- {{ 'CoreAdminHome_TrackAGoal'|translate }}
- </label>
+ <div piwik-field uicontrol="checkbox" name="image-tracking-goal-check"
+ ng-model="imageTrackingCode.trackGoal"
+ ng-change="imageTrackingCode.updateTrackingCode()"
+ disabled="imageTrackingCode.isLoading"
+ title="{{ 'CoreAdminHome_TrackAGoal'|translate|e('html_attr') }}"
+ value="">
</div>
- <div class="form-group" 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>
- <div class="input-group">
- <input type="text" class="revenue" value=""/>
- <span class="input-group-addon">{{ defaultSiteRevenue }}</span>
+
+ <div ng-show="imageTrackingCode.trackGoal"
+ id="image-tracking-goal-sub">
+ <div class="row">
+ <div class="col s12 m6">
+ <div piwik-field uicontrol="select" name="image-tracker-goal"
+ options="imageTrackingCode.allGoals"
+ disabled="imageTrackingCode.isLoading"
+ ng-model="imageTrackingCode.trackIdGoal"
+ full-width="true"
+ ng-change="imageTrackingCode.updateTrackingCode()"
+ value="">
+ </div>
+ </div>
+ <div class="col s12 m6">
+ <div piwik-field uicontrol="text" name="image-revenue"
+ ng-model="imageTrackingCode.revenue"
+ ng-change="imageTrackingCode.updateTrackingCode()"
+ disabled="imageTrackingCode.isLoading"
+ full-width="true"
+ title="{{ 'CoreAdminHome_WithOptionalRevenue'|translate|e('html_attr') }} <span class='site-currency'></span>"
+ value="">
+ </div>
+ </div>
</div>
</div>
- </div>
-
- <div id="image-link-output-section">
- <h3>{{ 'CoreAdminHome_ImageTrackingLink'|translate }}</h3>
+ <div id="image-link-output-section">
+ <h3>{{ 'CoreAdminHome_ImageTrackingLink'|translate }}</h3>
- <div id="image-tracking-text">
- <textarea class="codeblock"> </textarea>
+ <div id="image-tracking-text">
+ <pre piwik-select-on-focus
+ ng-bind="imageTrackingCode.trackingCode"> </pre>
+ </div>
</div>
</div>
-
</div>
-<h2>{{ 'CoreAdminHome_ImportingServerLogs'|translate }}</h2>
-
-<p>
- {{ 'CoreAdminHome_ImportingServerLogsDesc'|translate('<a href="http://piwik.org/log-analytics/" rel="noreferrer" target="_blank">','</a>')|raw }}
-</p>
-
+<div piwik-content-block content-title="{{ 'CoreAdminHome_ImportingServerLogs'|translate|e('html_attr') }}">
+ <p>
+ {{ 'CoreAdminHome_ImportingServerLogsDesc'|translate('<a href="http://piwik.org/log-analytics/" rel="noreferrer" target="_blank">','</a>')|raw }}
+ </p>
+</div>
{% endblock %}
diff --git a/plugins/CoreHome/Columns/UserId.php b/plugins/CoreHome/Columns/UserId.php
index 4534dac531..50d1be09bc 100644
--- a/plugins/CoreHome/Columns/UserId.php
+++ b/plugins/CoreHome/Columns/UserId.php
@@ -81,9 +81,11 @@ class UserId extends VisitDimension
$period = 'day';
}
- foreach ($idSites as $idSite) {
- if ($this->isUsedInSiteCached($idSite, $period, $date)) {
- return true;
+ if (!empty($idSites)) {
+ foreach ($idSites as $idSite) {
+ if ($this->isUsedInSiteCached($idSite, $period, $date)) {
+ return true;
+ }
}
}
diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index cf4be9b446..4cd94020ab 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -59,6 +59,12 @@ class Controller extends \Piwik\Plugin\Controller
return $report->render();
}
+ /**
+ * This is only used for exported widgets
+ * @return string
+ * @throws Exception
+ * @throws \Piwik\NoAccessException
+ */
public function renderWidgetContainer()
{
Piwik::checkUserHasSomeViewAccess();
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index e7b844db39..e7d8c52185 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -61,10 +61,13 @@ class CoreHome extends \Piwik\Plugin
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "libs/jquery/themes/base/jquery-ui.min.css";
+ $stylesheets[] = "libs/bower_components/materialize/dist/css/materialize.min.css";
$stylesheets[] = "libs/jquery/stylesheets/jquery.jscrollpane.css";
$stylesheets[] = "libs/jquery/stylesheets/scroll.less";
$stylesheets[] = "libs/bower_components/ngDialog/css/ngDialog.min.css";
$stylesheets[] = "libs/bower_components/ngDialog/css/ngDialog-theme-default.min.css";
+ $stylesheets[] = "plugins/Morpheus/stylesheets/base/bootstrap.css";
+ $stylesheets[] = "plugins/Morpheus/stylesheets/base/icons.css";
$stylesheets[] = "plugins/Morpheus/stylesheets/base.less";
$stylesheets[] = "plugins/Morpheus/stylesheets/main.less";
$stylesheets[] = "plugins/CoreHome/stylesheets/coreHome.less";
@@ -84,12 +87,16 @@ class CoreHome extends \Piwik\Plugin
$stylesheets[] = "plugins/CoreHome/angularjs/notification/notification.directive.less";
$stylesheets[] = "plugins/CoreHome/angularjs/quick-access/quick-access.directive.less";
$stylesheets[] = "plugins/CoreHome/angularjs/selector/selector.directive.less";
+ $stylesheets[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less";
+ $stylesheets[] = "plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.less";
+ $stylesheets[] = "plugins/CoreHome/angularjs/progressbar/progressbar.directive.less";
}
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "libs/bower_components/jquery/dist/jquery.min.js";
$jsFiles[] = "libs/bower_components/jquery-ui/ui/minified/jquery-ui.min.js";
+ $jsFiles[] = "libs/bower_components/materialize/dist/js/materialize.min.js";
$jsFiles[] = "libs/jquery/jquery.browser.js";
$jsFiles[] = "libs/jquery/jquery.truncate.js";
$jsFiles[] = "libs/bower_components/jquery.scrollTo/jquery.scrollTo.min.js";
@@ -105,8 +112,6 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "libs/bower_components/ngDialog/js/ngDialog.min.js";
$jsFiles[] = "plugins/Morpheus/javascripts/piwikHelper.js";
$jsFiles[] = "plugins/Morpheus/javascripts/ajaxHelper.js";
- $jsFiles[] = "plugins/Morpheus/javascripts/jquery.icheck.min.js";
- $jsFiles[] = "plugins/Morpheus/javascripts/morpheus.js";
$jsFiles[] = "plugins/Morpheus/javascripts/layout.js";
$jsFiles[] = "plugins/CoreHome/javascripts/require.js";
$jsFiles[] = "plugins/CoreHome/javascripts/uiControl.js";
@@ -124,6 +129,7 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/javascripts/notification.js";
$jsFiles[] = "plugins/CoreHome/javascripts/notification_parser.js";
$jsFiles[] = "plugins/CoreHome/javascripts/numberFormatter.js";
+ $jsFiles[] = "plugins/CoreHome/javascripts/zen-mode.js";
$jsFiles[] = "plugins/CoreHome/angularjs/piwikApp.config.js";
@@ -148,6 +154,7 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/attributes.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/field-condition.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/ignore-click.js";
@@ -155,6 +162,9 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/focusif.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/dialog.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/translate.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/dropdown-button.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/select-on-focus.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/side-nav.js";
$jsFiles[] = "plugins/CoreHome/angularjs/piwikApp.js";
$jsFiles[] = "plugins/CoreHome/angularjs/anchorLinkFix.js";
@@ -163,6 +173,8 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/history/history.service.js";
$jsFiles[] = "plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/progressbar/progressbar.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/alert/alert.directive.js";
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-model.service.js";
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.controller.js";
@@ -171,6 +183,8 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js";
$jsFiles[] = "plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/content-intro/content-intro.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/content-block/content-block.directive.js";
$jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.directive.js";
$jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.controller.js";
@@ -201,6 +215,18 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/quick-access/quick-access.directive.js";
$jsFiles[] = "plugins/CoreHome/angularjs/selector/selector.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/content-table/content-table.directive.js";
+
+ // we have to load these CoreAdminHome files here. If we loaded them in CoreAdminHome,
+ // there would be JS errors as CoreAdminHome is loaded first. Meaning it is loaded before
+ // any angular JS file is loaded etc.
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/smtp/mail-smtp.controller.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/branding/branding.controller.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingcode/jstrackingcode.controller.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingcode/imagetrackingcode.controller.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/archiving/archiving.controller.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.directive.js";
+ $jsFiles[] = "plugins/CoreAdminHome/angularjs/trustedhosts/trustedhosts.controller.js";
// we have to load these CorePluginsAdmin files here. If we loaded them in CorePluginsAdmin,
@@ -208,7 +234,16 @@ class CoreHome extends \Piwik\Plugin
// any angular JS file is loaded etc.
$jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js";
$jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/form/form.directive.js";
$jsFiles[] = "plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/field/field.directive.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugins/plugin-filter.directive.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugins/plugin-name.directive.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugins/plugin-management.directive.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.controller.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.directive.js";
+ $jsFiles[] = "plugins/CoreHome/javascripts/iframeResizer.min.js";
}
public function getClientSideTranslationKeys(&$translationKeys)
@@ -217,6 +252,7 @@ class CoreHome extends \Piwik\Plugin
$translationKeys[] = 'General_Loading';
$translationKeys[] = 'General_Show';
$translationKeys[] = 'General_Hide';
+ $translationKeys[] = 'General_Save';
$translationKeys[] = 'General_Website';
$translationKeys[] = 'Intl_Year_Short';
$translationKeys[] = 'General_MultiSitesSummary';
@@ -307,6 +343,7 @@ class CoreHome extends \Piwik\Plugin
$translationKeys[] = 'General_ErrorRequest';
$translationKeys[] = 'General_YourChangesHaveBeenSaved';
$translationKeys[] = 'General_LearnMore';
+ $translationKeys[] = 'General_ChooseDate';
$translationKeys[] = 'CoreHome_UndoPivotBySubtable';
$translationKeys[] = 'CoreHome_PivotBySubtable';
$translationKeys[] = 'General_LearnMore';
@@ -318,5 +355,6 @@ class CoreHome extends \Piwik\Plugin
$translationKeys[] = 'CoreHome_MainNavigation';
$translationKeys[] = 'CoreHome_ChangeCurrentWebsite';
$translationKeys[] = 'General_CreatedByUser';
+ $translationKeys[] = 'General_DateRangeFromTo';
}
}
diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
index 106685edf7..6c69019e05 100644
--- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
@@ -204,7 +204,6 @@ class RowEvolution
$view->config->show_goals = false;
$view->config->show_search = false;
$view->config->show_all_views_icons = false;
- $view->config->show_active_view_icon = false;
$view->config->show_related_reports = false;
$view->config->show_series_picker = false;
$view->config->show_footer_message = false;
diff --git a/plugins/CoreHome/Menu.php b/plugins/CoreHome/Menu.php
index cab21179a9..21bc6ac856 100644
--- a/plugins/CoreHome/Menu.php
+++ b/plugins/CoreHome/Menu.php
@@ -9,7 +9,6 @@
namespace Piwik\Plugins\CoreHome;
use Piwik\Db;
-use Piwik\Menu\MenuAdmin;
use Piwik\Menu\MenuTop;
use Piwik\Piwik;
use Piwik\Plugin;
@@ -28,13 +27,6 @@ class Menu extends \Piwik\Plugin\Menu
}
}
- public function configureAdminMenu(MenuAdmin $menu)
- {
- $menu->addPersonalItem(null, array(), 1, false);
- $menu->addManageItem(null, array(), 2, false);
- $menu->addPlatformItem(null, array(), 3, false);
- }
-
private function getLoginModule()
{
return Piwik::getLoginPluginName();
diff --git a/plugins/CoreHome/Widgets/GetDonateForm.php b/plugins/CoreHome/Widgets/GetDonateForm.php
index 081afd9c03..044a6897c3 100644
--- a/plugins/CoreHome/Widgets/GetDonateForm.php
+++ b/plugins/CoreHome/Widgets/GetDonateForm.php
@@ -36,13 +36,14 @@ class GetDonateForm extends Widget
public function render()
{
- $view = new View('@CoreHome/getDonateForm');
-
+ $footerMessage = null;
if (Common::getRequestVar('widget', false)
&& Piwik::hasUserSuperUserAccess()) {
- $view->footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess');
+ $footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess');
}
- return $view->render();
+ return $this->renderTemplate('getDonateForm', array(
+ 'footerMessage' => $footerMessage
+ ));
}
} \ No newline at end of file
diff --git a/plugins/CoreHome/Widgets/GetSystemSummary.php b/plugins/CoreHome/Widgets/GetSystemSummary.php
new file mode 100644
index 0000000000..9f09a57851
--- /dev/null
+++ b/plugins/CoreHome/Widgets/GetSystemSummary.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Widgets;
+
+use Piwik\API\Request;
+use Piwik\Db;
+use Piwik\Piwik;
+use Piwik\Plugin;
+use Piwik\Plugins\SegmentEditor\Services\StoredSegmentService;
+use Piwik\Version;
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+use Piwik\View;
+
+class GetSystemSummary extends Widget
+{
+ /**
+ * @var StoredSegmentService
+ */
+ private $storedSegmentService;
+
+ /**
+ * @var Plugin\Manager
+ */
+ private $pluginManager;
+
+ public function __construct(StoredSegmentService $storedSegmentService, Plugin\Manager $pluginManager)
+ {
+ $this->storedSegmentService = $storedSegmentService;
+ $this->pluginManager = $pluginManager;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('About Piwik');
+ $config->setName('CoreHome_SystemSummaryWidget');
+ $config->setOrder(15);
+ $config->setIsEnabled(Piwik::hasUserSuperUserAccess());
+ }
+
+ public function render()
+ {
+ $users = Request::processRequest('UsersManager.getUsers', array('filter_limit' => '-1'));
+ $websites = Request::processRequest('SitesManager.getAllSites', array('filter_limit' => '-1'));
+
+ return $this->renderTemplate('getSystemSummary', array(
+ 'numWebsites' => count($websites),
+ 'numUsers' => count($users),
+ 'numSegments' => $this->getNumSegments(),
+ 'numPlugins' => $this->getNumPlugins(),
+ 'piwikVersion' => Version::VERSION,
+ 'mySqlVersion' => $this->getMySqlVersion(),
+ 'phpVersion' => phpversion()
+ ));
+ }
+
+ private function getNumSegments()
+ {
+ $segments = $this->storedSegmentService->getAllSegmentsAndIgnoreVisibility();
+ return count($segments);
+ }
+
+ private function getMySqlVersion()
+ {
+ $db = Db::get();
+ return $db->getServerVersion();
+ }
+
+ private function getNumPlugins()
+ {
+ return count($this->pluginManager->getActivatedPlugins());
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
index 4417cea7ac..3eee13f206 100644
--- a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
+++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
@@ -11,7 +11,7 @@
* @param {Boolean} loading If true, the activity indicator is shown, otherwise the indicator is hidden.
*
* Example:
- * <div piwik-activity-indicator loading="true|false"></div>
+ * <div piwik-activity-indicator loading-message="'My custom message'" loading="true|false"></div>
*/
(function () {
angular.module('piwikApp').directive('piwikActivityIndicator', piwikActivityIndicator);
@@ -19,13 +19,24 @@
piwikActivityIndicator.$inject = ['piwik'];
function piwikActivityIndicator(piwik){
+
return {
restrict: 'A',
transclude: true,
scope: {
- loading: '='
+ loading: '=',
+ loadingMessage: '=?'
},
- templateUrl: 'plugins/CoreHome/angularjs/activity-indicator/activityindicator.html?cb=' + piwik.cacheBuster
+ templateUrl: 'plugins/CoreHome/angularjs/activity-indicator/activityindicator.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs) {
+ if (!scope.loadingMessage) {
+ scope.loadingMessage = _pk_translate('General_LoadingData');
+ }
+
+ };
+ }
};
}
})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
index f6080ae817..c925e83d86 100644
--- a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
+++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
@@ -1,3 +1,3 @@
<div ng-show="loading" class="loadingPiwik">
- <img src="plugins/Morpheus/images/loading-blue.gif" alt=""/>{{ 'General_LoadingData'|translate }}
+ <img src="plugins/Morpheus/images/loading-blue.gif" alt=""/> <span>{{ loadingMessage }}</span>
</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/alert/alert.directive.html b/plugins/CoreHome/angularjs/alert/alert.directive.html
new file mode 100644
index 0000000000..c45b3713d9
--- /dev/null
+++ b/plugins/CoreHome/angularjs/alert/alert.directive.html
@@ -0,0 +1,2 @@
+<div class="alert alert-{{severity}}" ng-transclude>
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/alert/alert.directive.js b/plugins/CoreHome/angularjs/alert/alert.directive.js
new file mode 100644
index 0000000000..e2687b1e4f
--- /dev/null
+++ b/plugins/CoreHome/angularjs/alert/alert.directive.js
@@ -0,0 +1,26 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-alert>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikAlert', piwikAlert);
+
+ piwikAlert.$inject = ['piwik'];
+
+ function piwikAlert(piwik){
+
+ return {
+ restrict: 'A',
+ transclude: true,
+ scope: {severity: '@piwikAlert'},
+ templateUrl: 'plugins/CoreHome/angularjs/alert/alert.directive.html?cb=' + piwik.cacheBuster
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/alert/alert.directive.less b/plugins/CoreHome/angularjs/alert/alert.directive.less
new file mode 100644
index 0000000000..694a0de484
--- /dev/null
+++ b/plugins/CoreHome/angularjs/alert/alert.directive.less
@@ -0,0 +1,124 @@
+.alert-icon-center-vertically(@font-size) {
+ @half-height: @font-size / 2;
+ // IE8 doesn't support calc(): it's OK, the icon will just be aligned to the top
+ top: calc(~'50% - @{half-height}');
+ // phantomjs only supports -webkit-calc()
+ top: -webkit-calc(~'50% - @{half-height}');
+}
+
+.alert {
+ padding: 20px 20px 20px 60px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 2px;
+ font-size: 14px;
+ position: relative;
+ &:before {
+ font-family: "piwik";
+ content: "\e625";
+ position: absolute;
+ left: 20px;
+ line-height: 100%; // line-height = font-size
+ font-size: 24px;
+ }
+
+ a {
+ color: inherit;
+ text-decoration: underline;
+ }
+}
+
+body #content .alert-success p {
+ color: #009874;
+}
+
+.alert-success {
+ color: #009874;
+ border-color: #1AA282;
+ &:before {
+ content: "\e63d";
+ color: #1AA282;
+ }
+ p {
+ color: #009874;
+ }
+ a {
+ color: #009874;
+ text-decoration: underline;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
+
+body #content .alert-info p {
+ color: #838383;
+}
+
+.alert-info {
+ color: #838383;
+ background-color: #F5F5F5;
+ font-size: 14px;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ p {
+ color: #838383;
+ }
+ &:before {
+ color: #afafaf;
+ font-size: 20px;
+ }
+ a {
+ color: #838383;
+ text-decoration: underline;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
+
+body #content .alert-warning p {
+ color: #CA8100;
+}
+
+.alert-warning {
+ color: #CA8100;
+ border-color: #DF9D27;
+ &:before {
+ content: "\e621";
+ color: #DF9D27;
+ }
+ p {
+ color: #CA8100;
+ }
+ a {
+ color: #CA8100;
+ text-decoration: underline;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
+
+body #content .alert-danger p {
+ color: #D4291F;
+}
+
+.alert-danger {
+ color: #D4291F;
+ border-color: #D73F36;
+ &:before {
+ content: "\e616";
+ color: #D73F36;
+ }
+ p {
+ color: #D4291F;
+ }
+ a {
+ color: #D4291F;
+ text-decoration: underline;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
diff --git a/plugins/CoreHome/angularjs/common/directives/attributes.js b/plugins/CoreHome/angularjs/common/directives/attributes.js
index c8285d9e8e..43de0fadcd 100644
--- a/plugins/CoreHome/angularjs/common/directives/attributes.js
+++ b/plugins/CoreHome/angularjs/common/directives/attributes.js
@@ -24,13 +24,29 @@
return {
link: function (scope, element, attrs) {
- attrs.piwikAttributes = JSON.parse(attrs.piwikAttributes);
+ if (!attrs.piwikAttributes || !angular.isString(attrs.piwikAttributes)) {
+ return;
+ }
- if (angular.isObject(attrs.piwikAttributes)) {
- angular.forEach(attrs.piwikAttributes, function (value, key) {
- element.attr(key, value);
- });
+ function applyAttributes(attributes)
+ {
+ if (angular.isObject(attributes)) {
+ angular.forEach(attributes, function (value, key) {
+ if (key === 'disabled') {
+ element.prop(key, value);
+ } else {
+ element.attr(key, value);
+ }
+ });
+ }
}
+
+ applyAttributes(JSON.parse(attrs.piwikAttributes));
+
+ attrs.$observe('piwikAttributes', function (newVal) {
+ applyAttributes(JSON.parse(newVal));
+ });
+
}
};
}
diff --git a/plugins/CoreHome/angularjs/common/directives/dialog.js b/plugins/CoreHome/angularjs/common/directives/dialog.js
index cce88292df..5ffa3cb513 100644
--- a/plugins/CoreHome/angularjs/common/directives/dialog.js
+++ b/plugins/CoreHome/angularjs/common/directives/dialog.js
@@ -28,12 +28,6 @@
element.css('display', 'none');
- element.on( "dialogclose", function() {
- setTimeout(function () {
- scope.$apply($parse(attrs.piwikDialog).assign(scope, false));
- }, 0);
- });
-
scope.$watch(attrs.piwikDialog, function(newValue, oldValue) {
if (newValue) {
piwik.helper.modalConfirm(element, {yes: function() {
@@ -41,7 +35,13 @@
scope.$eval(attrs.yes);
setTimeout(function () { scope.$apply(); }, 0);
}
- }});
+ }}, {
+ complete: function () {
+ setTimeout(function () {
+ scope.$apply($parse(attrs.piwikDialog).assign(scope, false));
+ }, 0);
+ }
+ });
}
});
}
diff --git a/plugins/CoreHome/angularjs/common/directives/dropdown-button.js b/plugins/CoreHome/angularjs/common/directives/dropdown-button.js
new file mode 100644
index 0000000000..6513093d8e
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/directives/dropdown-button.js
@@ -0,0 +1,37 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-dropdown-button>
+ */
+(function () {
+ angular.module('piwikApp.directive').directive('dropdownButton', piwikDropdownButton);
+
+ piwikDropdownButton.$inject = ['piwik'];
+
+ function piwikDropdownButton(piwik){
+
+ return {
+ restrict: 'C',
+ compile: function (element, attrs) {
+
+ $(element).dropdown({
+ inDuration: 300,
+ outDuration: 225,
+ constrain_width: false, // Does not change width of dropdown to that of the activator
+ // hover: true, // Activate on hover
+ belowOrigin: true // Displays dropdown below the button
+ });
+
+ return function (scope, element, attrs) {
+
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/directives/field-condition.js b/plugins/CoreHome/angularjs/common/directives/field-condition.js
new file mode 100644
index 0000000000..5d936c4480
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/directives/field-condition.js
@@ -0,0 +1,89 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-form-condition>
+ */
+(function () {
+ angular.module('piwikApp.directive').directive('fieldCondition', piwikFieldCondition);
+
+ piwikFieldCondition.$inject = ['piwik', '$timeout'];
+
+ function piwikFieldCondition(piwik, $timeout){
+
+ function evaluate(scope, condition, element)
+ {
+ if (scope.$eval(condition, scope.allValues)) {
+ element.show();
+ } else {
+ element.hide();
+ }
+ }
+
+ function getValueFromElement(element)
+ {
+ if (element.attr('type') === 'checkbox') {
+ return element.is(':checked')
+ } else if (element.attr('type') === 'radio') {
+ var name = element.attr('name');
+ return $('.form-group [name=' + name + ']:checked').val()
+ } else if (element.prop('tagName').toLowerCase() === 'select') {
+ var name = element.val();
+ if (name.indexOf('string:') === 0) {
+ return name.substr('string:'.length);
+ }
+
+ return name;
+ }
+
+ return element.val();
+ }
+
+ function evaluateConditionalExpression(scope, condition, element)
+ {
+ var fieldParts = condition.replace('!', '');
+ fieldParts = fieldParts.split(' ');
+ var fieldNames = [];
+ fieldParts.forEach(function (name) {
+ name = $.trim(name);
+ if (name && name.length > 3) {
+ fieldNames.push(name);
+ }
+ });
+
+ scope.allValues = {};
+ angular.forEach(fieldNames, function (name) {
+ var actualField = $('.form-group [name=' + name + ']').first();
+ if (actualField.size()) {
+ scope.allValues[name] = getValueFromElement(actualField);
+ actualField.on('change', function () {
+ scope.allValues[name] = getValueFromElement($(this));
+ evaluate(scope, condition, element);
+ })
+ }
+ });
+
+ evaluate(scope, condition, element);
+ }
+
+
+ return {
+ priority: 10, // makes sure to render after other directives, otherwise the content might be overwritten again see https://github.com/piwik/piwik/pull/8467
+ restrict: 'A',
+ link: function(scope, element, attrs) {
+
+ var condition = attrs.fieldCondition;
+ if (condition) {
+ $timeout(function (){
+ evaluateConditionalExpression(scope, condition, element);
+ });
+ }
+ },
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/directives/select-on-focus.js b/plugins/CoreHome/angularjs/common/directives/select-on-focus.js
new file mode 100644
index 0000000000..bcaf868f2d
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/directives/select-on-focus.js
@@ -0,0 +1,63 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * On focus (click, tab) selects the text within the current element
+ *
+ * Example:
+ * <div piwik-select-on-focus>my dialog</div>
+ */
+(function () {
+ angular.module('piwikApp.directive').directive('piwikSelectOnFocus', piwikSelectOnFocus);
+
+ function piwikSelectOnFocus(){
+ return {
+ restrict: 'A',
+ link: function(scope, element, attr, ctrl) {
+
+ var focusedElement = null;
+
+ var tagName = (element.prop('tagName') + '').toLowerCase();
+ var elementSupportsSelect = tagName === 'textarea';
+
+ function onFocusHandler(event) {
+ if (focusedElement !== this) {
+ focusedElement = this;
+ angular.element(this).select();
+ }
+ }
+
+ function onClickHandler(event) {
+ // .select() + focus and blur seems to not work on pre elements
+ var range = document.createRange();
+ range.selectNode(this);
+ window.getSelection().addRange(range);
+ }
+
+ function onBlurHandler(event) {
+ focusedElement = null;
+ }
+
+ if (elementSupportsSelect) {
+ element.on('focus', onFocusHandler);
+ element.on('blur', onBlurHandler);
+ } else {
+ element.on('click', onClickHandler);
+ }
+
+ scope.$on('$destroy', function() {
+ if (elementSupportsSelect) {
+ element.off('focus', onFocusHandler);
+ element.off('blur', onBlurHandler);
+ } else {
+ element.off('click', onClickHandler);
+ }
+ });
+ }
+ };
+ }
+})();
diff --git a/plugins/CoreHome/angularjs/common/directives/side-nav.js b/plugins/CoreHome/angularjs/common/directives/side-nav.js
new file mode 100644
index 0000000000..295a95838a
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/directives/side-nav.js
@@ -0,0 +1,49 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Will activate the materialize side nav feature once rendered. We use this directive as it makes sure
+ * the actual left menu is rendered at the time we init the side nav.
+ *
+ * Has to be set on a collaapsible element
+ *
+ * Example:
+ * <div class="collapsible" piwik-side-nav="nav .activateLeftMenu">...</div>
+ */
+(function () {
+ angular.module('piwikApp.directive').directive('piwikSideNav', piwikSideNav);
+
+ piwikSideNav.$inject = ['$timeout'];
+ var initialized = false;
+
+ function piwikSideNav($timeout){
+ return {
+ restrict: 'A',
+ priority: 10,
+ link: function(scope, element, attr, ctrl) {
+
+ if (attr.piwikSideNav) {
+ $timeout(function () {
+ if (!initialized) {
+ initialized = true;
+
+ var sideNavActivator = $(attr.piwikSideNav).show();
+
+ sideNavActivator.sideNav({
+ closeOnClick: true
+ });
+ }
+
+ if (element.hasClass('collapsible')) {
+ element.collapsible();
+ }
+ });
+ }
+ }
+ };
+ }
+})();
diff --git a/plugins/CoreHome/angularjs/common/services/piwik-api.js b/plugins/CoreHome/angularjs/common/services/piwik-api.js
index 5257aff0c9..aeed3bfe2c 100644
--- a/plugins/CoreHome/angularjs/common/services/piwik-api.js
+++ b/plugins/CoreHome/angularjs/common/services/piwik-api.js
@@ -121,10 +121,15 @@ var hasBlockedContent = false;
'cache-control': 'no-cache'
};
+ var requestFormat = format;
+ if (getParams.format && getParams.format.toLowerCase() !== 'json' && getParams.format.toLowerCase() !== 'json2') {
+ requestFormat = getParams.format;
+ }
+
var ajaxCall = {
method: 'POST',
url: url,
- responseType: format,
+ responseType: requestFormat,
params: _mixinDefaultGetParams(getParams),
data: $.param(getPostParams(postParams)),
timeout: requestPromise,
@@ -242,7 +247,10 @@ var hasBlockedContent = false;
function fetch (getParams, options) {
getParams.module = getParams.module || 'API';
- getParams.format = 'JSON2';
+
+ if (!getParams.format) {
+ getParams.format = 'JSON2';
+ }
addParams(getParams, 'GET');
@@ -255,6 +263,9 @@ var hasBlockedContent = false;
function post(getParams, _postParams_, options) {
if (_postParams_) {
+ if (postParams && postParams.token_auth && !_postParams_.token_auth) {
+ _postParams_.token_auth = postParams.token_auth;
+ }
postParams = _postParams_;
}
diff --git a/plugins/CoreHome/angularjs/content-block/content-block.directive.html b/plugins/CoreHome/angularjs/content-block/content-block.directive.html
new file mode 100644
index 0000000000..547ea79878
--- /dev/null
+++ b/plugins/CoreHome/angularjs/content-block/content-block.directive.html
@@ -0,0 +1,10 @@
+<div class="card">
+ <div class="card-content">
+ <h2 ng-if="contentTitle && !feature && !helpUrl && !helpText" class="card-title">{{contentTitle}}</h2>
+ <h2 ng-if="contentTitle && (feature || helpUrl || helpText)" class="card-title"
+ piwik-enriched-headline feature-name="{{feature}}" help-url="{{helpUrl}}" inline-help="{{ helpText }}">
+ {{contentTitle}}</h2>
+ <div ng-transclude>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/content-block/content-block.directive.js b/plugins/CoreHome/angularjs/content-block/content-block.directive.js
new file mode 100644
index 0000000000..ab4f2e5082
--- /dev/null
+++ b/plugins/CoreHome/angularjs/content-block/content-block.directive.js
@@ -0,0 +1,75 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-content-block>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikContentBlock', piwikContentBlock);
+
+ piwikContentBlock.$inject = ['piwik'];
+
+ function piwikContentBlock(piwik){
+
+ var contentTopPosition = null;
+
+ return {
+ restrict: 'A',
+ replace: true,
+ transclude: true,
+ scope: {
+ contentTitle: '@',
+ feature: '@',
+ helpUrl: '@',
+ helpText: '@'
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/content-block/content-block.directive.html?cb=' + piwik.cacheBuster,
+ controllerAs: 'contentBlock',
+ compile: function (element, attrs) {
+
+ if (attrs.feature === 'true') {
+ attrs.feature = true;
+ }
+
+ return function (scope, element, attrs) {
+ var inlineHelp = element.find('[ng-transclude] > .contentHelp');
+ if (inlineHelp.size()) {
+ scope.helpText = inlineHelp.html();
+ inlineHelp.remove();
+ }
+
+ if (scope.feature && (scope.feature===true || scope.feature ==='true')) {
+ scope.feature = scope.contentTitle;
+ }
+
+ if (contentTopPosition !== false) {
+ if (contentTopPosition === null) {
+ var $content = $('#content.admin');
+ if ($content.size()) {
+ // cache top position for further content blocks
+ contentTopPosition = $content.offset().top;
+ } else {
+ contentTopPosition = false;
+ }
+ }
+
+ if (contentTopPosition || contentTopPosition === 0) {
+ var topThis = element.offset().top;
+ if ((topThis - contentTopPosition) < 17) {
+ // we make sure to display the first card with no margin-top to have it on same as line as
+ // navigation
+ element.css('marginTop', '0');
+ }
+ }
+ }
+
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/content-intro/content-intro.directive.js b/plugins/CoreHome/angularjs/content-intro/content-intro.directive.js
new file mode 100644
index 0000000000..cd9cd53a48
--- /dev/null
+++ b/plugins/CoreHome/angularjs/content-intro/content-intro.directive.js
@@ -0,0 +1,26 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-content-block>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikContentIntro', piwikContentIntro);
+
+ piwikContentIntro.$inject = ['piwik'];
+
+ function piwikContentIntro(piwik){
+
+ return {
+ restrict: 'A',
+ compile: function (element, attrs) {
+ element.addClass('piwik-content-intro');
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/content-table/content-table.directive.js b/plugins/CoreHome/angularjs/content-table/content-table.directive.js
new file mode 100644
index 0000000000..f20329b8ce
--- /dev/null
+++ b/plugins/CoreHome/angularjs/content-table/content-table.directive.js
@@ -0,0 +1,30 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-content-table>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikContentTable', piwikContentTable);
+
+ piwikContentTable.$inject = ['piwik'];
+
+ function piwikContentTable(piwik){
+
+ return {
+ restrict: 'A',
+ compile: function (element, attrs) {
+ element.addClass('card card-table entityTable');
+
+ return function (scope, element, attrs) {
+
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.html b/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.html
index f1a6779c87..719545a6ac 100644
--- a/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.html
+++ b/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.html
@@ -5,7 +5,7 @@
title="{{ 'CoreHome_ClickToEditX'|translate:featureName }}"
ng-transclude ></a>
- <span ng-show="view.showIcons">
+ <span ng-show="view.showIcons" class="iconsBar">
<a ng-if="helpUrl && !inlineHelp"
rel="noreferrer"
target="_blank"
diff --git a/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.js b/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.js
index ea248e1360..e85d253b70 100644
--- a/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.js
+++ b/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.js
@@ -37,8 +37,7 @@
function piwikEnrichedHeadline($document, piwik, $filter){
var defaults = {
helpUrl: '',
- editUrl: '',
- inlineHelp: ''
+ editUrl: ''
};
return {
@@ -48,7 +47,7 @@
helpUrl: '@',
editUrl: '@',
featureName: '@',
- inlineHelp: '@'
+ inlineHelp: '@?'
},
templateUrl: 'plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.html?cb=' + piwik.cacheBuster,
compile: function (element, attrs) {
@@ -62,6 +61,11 @@
var helpNode = $('[ng-transclude] .inlineHelp', element);
+ if ((!helpNode || !helpNode.length) && element.next()) {
+ // hack for reports :(
+ helpNode = element.next().find('.reportDocumentation');
+ }
+
if (helpNode && helpNode.length) {
if ($.trim(helpNode.text())) {
scope.inlineHelp = $.trim(helpNode.html());
diff --git a/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less b/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less
index 4ffb8326f0..d054809401 100644
--- a/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less
+++ b/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less
@@ -20,7 +20,6 @@
}
.title {
- color: @color-black-piwik;
display:inline-block;
}
@@ -32,8 +31,7 @@
border: 1px solid #E4E5E4;
margin: 10px 0 10px 0;
padding: 10px;
- border-radius: 4px;
- max-width: 500px;
+ border-radius: 2px;
.readMore {
margin-top: 10px;
@@ -41,10 +39,13 @@
font-weight: bold;
}
}
+ .iconsBar {
+ line-height: 1 !important;
+ }
.ratingIcons {
display: inline-block;
- vertical-align: bottom;
+ vertical-align: middle;
}
.helpIcon {
@@ -53,8 +54,10 @@
margin: 0 0 -1px 4px;
opacity: 0.3;
font-size: 15px;
+ color: @theme-color-text-light;
&:hover {
opacity: 0.9;
+ text-decoration: none;
}
}
}
diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
index b9084dbe10..cbfbbb1ae1 100644
--- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
+++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
@@ -2,10 +2,12 @@
<span class="title"
ng-click="view.showItems=!view.showItems"
- title="{{ tooltip }}"
- ng-bind-html="menuTitle"/>
+ title="{{ tooltip }}">
+ <span ng-bind-html="menuTitle"></span>
+ <span class="icon-arrow-bottom"></span>
+ </span>
- <div class="items borderedControl" ng-show="view.showItems">
+ <div class="items" ng-show="view.showItems">
<div class="search" ng-if="showSearch && view.showItems">
<input type="text"
piwik-focus-if="view.showItems"
diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less
index a36d00c0f5..f33208a3f9 100644
--- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less
+++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less
@@ -2,33 +2,17 @@
.menuDropdown {
display: inline-block;
padding-right: 14px;
- color: @theme-color-link;
.title {
position: relative;
cursor: pointer;
-
- &:after {
- content: '';
- position: absolute;
- top: 5px;
- right: -15px;
- color: @theme-color-link;
- display: inline;
- font-size: 1px;
- height: 0px;
- width: 0px;
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
- border-top: 5px solid @theme-color-link;
- }
}
.items {
z-index: 200;
position: absolute;
border: 1px solid @color-silver-l80;
- background: @theme-color-background-base;
+ background: @theme-color-background-contrast;
max-height: 400px;
overflow-y: auto;
overflow-x: hidden;
@@ -72,6 +56,7 @@
font-size: 11px;
float: none;
text-align: left;
+ line-height: 30px;
&:hover {
background: @theme-color-background-tinyContrast;
diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.less b/plugins/CoreHome/angularjs/notification/notification.directive.less
index ea72abe4f1..cb47f28c10 100644
--- a/plugins/CoreHome/angularjs/notification/notification.directive.less
+++ b/plugins/CoreHome/angularjs/notification/notification.directive.less
@@ -1,6 +1,9 @@
.system.notification {
.alert;
+ box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
+ border: 0 !important;
+
// We have to use !important because the default button style is crazy
.close {
position: relative;
@@ -20,15 +23,19 @@
&.notification-success {
.alert-success;
+ background-color: #e8f5e9;
}
&.notification-warning {
.alert-warning;
+ background-color: #fff3e0;
}
&.notification-danger,
&.notification-error {
.alert-danger;
+ background-color: #ffebee;
}
&.notification-info {
.alert-info;
+ background-color: #e3f2fd;
}
} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/progressbar/progressbar.directive.html b/plugins/CoreHome/angularjs/progressbar/progressbar.directive.html
new file mode 100644
index 0000000000..5bbb4e4400
--- /dev/null
+++ b/plugins/CoreHome/angularjs/progressbar/progressbar.directive.html
@@ -0,0 +1,7 @@
+<div class="progressbar">
+ <div class="progress">
+ <div class="determinate" style="width: 0"
+ ng-style="{width: (progress + '%')}"></div>
+ </div>
+ <span ng-show="!!label"><img src='./plugins/Morpheus/images/loading-blue.gif'/> <span ng-bind-html="label"></span></span>
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/progressbar/progressbar.directive.js b/plugins/CoreHome/angularjs/progressbar/progressbar.directive.js
new file mode 100644
index 0000000000..445fa1d021
--- /dev/null
+++ b/plugins/CoreHome/angularjs/progressbar/progressbar.directive.js
@@ -0,0 +1,54 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-progressbar>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikProgressbar', piwikProgressbar);
+
+ piwikProgressbar.$inject = ['piwik'];
+
+ function piwikProgressbar(piwik){
+ var defaults = {
+ label: '',
+ progress: 0
+ };
+
+ return {
+ restrict: 'A',
+ scope: {
+ progress: '=',
+ label: '='
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/progressbar/progressbar.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ for (var index in defaults) {
+ if (defaults.hasOwnProperty(index) && attrs[index] === undefined) {
+ attrs[index] = defaults[index];
+ }
+ }
+
+ return function (scope, element, attrs) {
+
+ scope.$watch('progress', function (val, oldVal) {
+ if (val !== oldVal) {
+ if (val > 100) {
+ scope.progress = 100;
+ } else if (val < 0) {
+ scope.progress = 0;
+ }
+ }
+ });
+
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/progressbar/progressbar.directive.less b/plugins/CoreHome/angularjs/progressbar/progressbar.directive.less
new file mode 100644
index 0000000000..70a02c8247
--- /dev/null
+++ b/plugins/CoreHome/angularjs/progressbar/progressbar.directive.less
@@ -0,0 +1,3 @@
+.progressbar {
+ margin: 22px 24px;
+} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/quick-access/quick-access.controller.js b/plugins/CoreHome/angularjs/quick-access/quick-access.controller.js
index c78628036b..fbcd5f17ee 100644
--- a/plugins/CoreHome/angularjs/quick-access/quick-access.controller.js
+++ b/plugins/CoreHome/angularjs/quick-access/quick-access.controller.js
@@ -82,5 +82,9 @@
$scope.selectMenuItem(index);
};
+ if (typeof initTopControls !== 'undefined' && initTopControls) {
+ initTopControls();
+ }
+
}
})();
diff --git a/plugins/CoreHome/angularjs/quick-access/quick-access.directive.html b/plugins/CoreHome/angularjs/quick-access/quick-access.directive.html
index 18d4eee252..e01284a10e 100644
--- a/plugins/CoreHome/angularjs/quick-access/quick-access.directive.html
+++ b/plugins/CoreHome/angularjs/quick-access/quick-access.directive.html
@@ -1,4 +1,4 @@
-<div class="quick-access"
+<div class="quick-access piwikSelector"
ng-class="{active: view.searchActive, expanded: view.searchActive}"
piwik-focus-anywhere-but-here="view.searchActive = false;">
<span class="icon-search" ng-hide="search.term || view.searchActive"
@@ -10,29 +10,31 @@
ng-focus="view.searchActive=true"
ng-model="search.term" piwik-focus-if="view.searchActive"
type="text" tabindex="2"/>
- <ul ng-hide="!search.term || !view.searchActive || (quickAccess.numMenuItems > 0) || (quickAccess.sitesModel.sites | length)">
- <li class="no-result">{{ 'General_SearchNoResults' | translate }}</li>
- </ul>
- <ul ng-show="search.term && view.searchActive" ng-repeat="subcategory in quickAccess.menuItems">
- <li class="quick-access-category"
- ng-click="search.term = subcategory.title;quickAccess.searchMenu(search.term)">{{ subcategory.title }}</li>
- <li class="result"
- ng-class="{selected: submenuEntry.menuIndex == search.index}"
- ng-mouseenter="search.index=submenuEntry.menuIndex"
- ng-click="quickAccess.selectMenuItem(submenuEntry.index)"
- ng-repeat="submenuEntry in subcategory.items"><a>{{ submenuEntry.name | trim }}</a></li>
- </ul>
- <ul ng-show="search.term && view.searchActive">
- <li class="quick-access-category websiteCategory"
- ng-show="hasSitesSelector && ((quickAccess.sitesModel.sites | length) || quickAccess.sitesModel.isLoading)"
- >{{ 'SitesManager_Sites' | translate }}</li>
- <li class="no-result"
- ng-show="hasSitesSelector && quickAccess.sitesModel.isLoading">{{ 'MultiSites_LoadingWebsites' | translate }}</li>
- <li class="result"
- ng-show="hasSitesSelector && !quickAccess.sitesModel.isLoading"
- ng-mouseenter="search.index=(quickAccess.numMenuItems + $index)"
- ng-class="{selected: (quickAccess.numMenuItems + $index) == search.index}"
- ng-click="quickAccess.selectSite(site.idsite)"
- ng-repeat="site in quickAccess.sitesModel.sites"><a ng-bind-html="site.name"></a></li>
- </ul>
+ <div class="dropdown" ng-show="search.term || view.searchActive">
+ <ul ng-hide="(quickAccess.numMenuItems > 0) || (quickAccess.sitesModel.sites | length)">
+ <li class="no-result">{{ 'General_SearchNoResults' | translate }}</li>
+ </ul>
+ <ul ng-repeat="subcategory in quickAccess.menuItems">
+ <li class="quick-access-category"
+ ng-click="search.term = subcategory.title;quickAccess.searchMenu(search.term)">{{ subcategory.title }}</li>
+ <li class="result"
+ ng-class="{selected: submenuEntry.menuIndex == search.index}"
+ ng-mouseenter="search.index=submenuEntry.menuIndex"
+ ng-click="quickAccess.selectMenuItem(submenuEntry.index)"
+ ng-repeat="submenuEntry in subcategory.items"><a>{{ submenuEntry.name | trim }}</a></li>
+ </ul>
+ <ul>
+ <li class="quick-access-category websiteCategory"
+ ng-show="hasSitesSelector && ((quickAccess.sitesModel.sites | length) || quickAccess.sitesModel.isLoading)"
+ >{{ 'SitesManager_Sites' | translate }}</li>
+ <li class="no-result"
+ ng-show="hasSitesSelector && quickAccess.sitesModel.isLoading">{{ 'MultiSites_LoadingWebsites' | translate }}</li>
+ <li class="result"
+ ng-show="hasSitesSelector && !quickAccess.sitesModel.isLoading"
+ ng-mouseenter="search.index=(quickAccess.numMenuItems + $index)"
+ ng-class="{selected: (quickAccess.numMenuItems + $index) == search.index}"
+ ng-click="quickAccess.selectSite(site.idsite)"
+ ng-repeat="site in quickAccess.sitesModel.sites"><a ng-bind-html="site.name"></a></li>
+ </ul>
+ </div>
</div>
diff --git a/plugins/CoreHome/angularjs/quick-access/quick-access.directive.js b/plugins/CoreHome/angularjs/quick-access/quick-access.directive.js
index 619fedfc2b..b71a468a7d 100644
--- a/plugins/CoreHome/angularjs/quick-access/quick-access.directive.js
+++ b/plugins/CoreHome/angularjs/quick-access/quick-access.directive.js
@@ -71,7 +71,7 @@
var category = _pk_translate('CoreHome_Menu');
- $rootElement.find('#topRightBar .navbar-right li > a').each(function (index, element) {
+ $rootElement.find('nav .side-nav li > a').each(function (index, element) {
var $element = $(element);
var text = trim($element.text());
diff --git a/plugins/CoreHome/angularjs/quick-access/quick-access.directive.less b/plugins/CoreHome/angularjs/quick-access/quick-access.directive.less
index 1c46c7533d..d0ec71ec98 100644
--- a/plugins/CoreHome/angularjs/quick-access/quick-access.directive.less
+++ b/plugins/CoreHome/angularjs/quick-access/quick-access.directive.less
@@ -1,6 +1,13 @@
.quick-access {
position: relative;
+ &:hover,
+ &.expanded,
+ &.active {
+ input {
+ background-color: @theme-color-background-contrast !important;
+ }
+ }
li {
font-size: 11px;
}
@@ -22,9 +29,9 @@
input {
width:100%;
height: 100%;
- border: 0 !important;
box-shadow: 0 0 !important;
border-radius: 0 !important;
+ background-color: @theme-color-background-base !important;
font-size: 11px;
&:focus {
outline: none;
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
index add0b3fc8b..f82eca4038 100644
--- a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
@@ -11,6 +11,11 @@
function ReportingMenuController($scope, piwik, $location, $timeout, menuModel, $rootScope) {
+ var idSite = getUrlParam('idSite');
+ var period = getUrlParam('period');
+ var date = getUrlParam('date');
+ var segment = getUrlParam('segment');
+
function markAllCategoriesAsInactive()
{
angular.forEach(menuModel.menu, function (cat) {
@@ -62,11 +67,6 @@
}
};
- var idSite = getUrlParam('idSite');
- var period = getUrlParam('period');
- var date = getUrlParam('date');
- var segment = getUrlParam('segment');
-
$scope.makeUrl = function (category, subcategory) {
var url = 'idSite=' + encodeURIComponent(idSite);
@@ -123,6 +123,10 @@
var category = $location.search().category;
var subcategory = $location.search().subcategory;
+ period = getUrlParam('period');
+ date = getUrlParam('date');
+ segment = getUrlParam('segment');
+
if (!category || !subcategory) {
return;
}
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
index 0e32b23d7f..981bc09d03 100644
--- a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
@@ -1,4 +1,4 @@
-<ul class="navbar" role="menu" aria-label="{{ 'CoreHome_MainNavigation'|translate }}">
+<div><ul class="navbar hide-on-med-and-down" role="menu" aria-label="{{ 'CoreHome_MainNavigation'|translate }}">
<li ng-repeat="category in menuModel.menu"
class="menuTab"
role="menuitem"
@@ -35,4 +35,28 @@
</li>
</ul>
</li>
-</ul> \ No newline at end of file
+</ul>
+<ul id="mobile-left-menu" class="side-nav hide-on-large-only">
+ <li class="no-padding" ng-repeat="category in menuModel.menu">
+ <ul class="collapsible collapsible-accordion" piwik-side-nav="nav .activateLeftMenu">
+ <li>
+ <a class="collapsible-header">{{ category.name }}<i class="{{ category.icon ? category.icon : 'icon-arrow-bottom' }}"></i></a>
+ <div class="collapsible-body">
+ <ul>
+ <li ng-repeat="subcategory in category.subcategories">
+ <a ng-if="subcategory.isGroup"
+ ng-repeat="subcat in subcategory.subcategories"
+ ng-click='loadSubcategory(category, subcat)'
+ ng-href="#?{{ makeUrl(category,subcat) }}">{{ subcat.name }}</a>
+ <a ng-if="!subcategory.isGroup"
+ ng-click='loadSubcategory(category, subcategory)'
+ ng-href="#?{{ makeUrl(category,subcategory) }}">{{ subcategory.name }}</a>
+ </li>
+ </ul>
+ </div>
+ </li>
+ </ul>
+ </li>
+</ul>
+
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
index 3fe5955dc7..8bf2590abe 100644
--- a/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
@@ -15,6 +15,18 @@
var currentCategory = null;
var currentSubcategory = null;
+ var currentPeriod = null;
+ var currentDate = null;
+ var currentSegment = null;
+
+ function renderInitialPage()
+ {
+ var $search = $location.search();
+ currentPeriod = $search.period;
+ currentDate = $search.date;
+ currentSegment = $search.segment;
+ $scope.renderPage($search.category, $search.subcategory);
+ }
$scope.renderPage = function (category, subcategory) {
if (!category || !subcategory) {
@@ -23,6 +35,8 @@
return;
}
+ $rootScope.$emit('piwikPageChange', {});
+
currentCategory = category;
currentSubcategory = subcategory;
@@ -45,19 +59,32 @@
}
$scope.loading = true; // we only set loading on initial load
-
- $scope.renderPage($location.search().category, $location.search().subcategory);
+
+ renderInitialPage();
$rootScope.$on('$locationChangeSuccess', function () {
+ var $search = $location.search();
+
// should be handled by $route
- var category = $location.search().category;
- var subcategory = $location.search().subcategory;
+ var category = $search.category;
+ var subcategory = $search.subcategory;
+ var period = $search.period;
+ var date = $search.date;
+ var segment = $search.segment;
- if (category === currentCategory && subcategory === currentSubcategory) {
+ if (category === currentCategory
+ && subcategory === currentSubcategory
+ && period === currentPeriod
+ && date === currentDate
+ && segment === currentSegment) {
// this page is already loaded
return;
}
+ currentPeriod = period;
+ currentDate = date;
+ currentSegment = segment;
+
$scope.renderPage(category, subcategory);
});
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
index 9a8974e313..85739f4c45 100644
--- a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
@@ -5,20 +5,20 @@
<div ng-show="hasNoPage">{{ 'CoreHome_NoSuchPage'|translate }}</div>
<div class="row" ng-repeat="evolutionReport in pageModel.evolutionReports">
- <div class="col-md-12" piwik-widget="evolutionReport"></div>
+ <div class="col s12 fullWidgetColumn" piwik-widget="evolutionReport"></div>
</div>
<div class="row" ng-repeat="sparklineReport in pageModel.sparklineReports">
- <div class="col-md-12" piwik-widget="sparklineReport"></div>
+ <div class="col s12 fullWidgetColumn" piwik-widget="sparklineReport"></div>
</div>
<div class="row" ng-repeat="widget in pageModel.widgets">
- <div class="col-md-12" ng-if="!widget.group" piwik-widget="widget"></div>
+ <div class="col s12 fullWidgetColumn" ng-if="!widget.group" piwik-widget="widget"></div>
- <div ng-if="widget.group" class="col-md-6">
+ <div ng-if="widget.group" class="col s12 l6 leftWidgetColumn">
<div ng-repeat="widgetInGroup in widget.left" piwik-widget="widgetInGroup"></div>
</div>
- <div ng-if="widget.group" class="col-md-6">
+ <div ng-if="widget.group" class="col s12 l6 rightWidgetColumn">
<div ng-repeat="widgetInGroup in widget.right" piwik-widget="widgetInGroup"></div>
</div>
</div>
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less
new file mode 100644
index 0000000000..1d9fde17b9
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less
@@ -0,0 +1,24 @@
+.reporting-page {
+ > .row {
+ margin-bottom: 0;
+ }
+
+ .fullWidgetColumn {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .leftWidgetColumn {
+ padding-left: 0;
+ }
+
+ .rightWidgetColumn {
+ padding-right: 0;
+ }
+
+ .isFirstWidgetInPage {
+ .card {
+ margin-top: 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/selector/selector.directive.js b/plugins/CoreHome/angularjs/selector/selector.directive.js
index 37db1d5743..7779ce37a0 100644
--- a/plugins/CoreHome/angularjs/selector/selector.directive.js
+++ b/plugins/CoreHome/angularjs/selector/selector.directive.js
@@ -11,9 +11,9 @@
(function () {
angular.module('piwikApp').directive('piwikExpandOnClick', piwikExpandOnClick);
- piwikExpandOnClick.$inject = ['$document'];
+ piwikExpandOnClick.$inject = ['$document', 'piwik'];
- function piwikExpandOnClick($document){
+ function piwikExpandOnClick($document, piwik){
return {
restrict: 'A',
@@ -21,6 +21,12 @@
element.find('.title').on('click', function () {
element.toggleClass('expanded');
+
+ var $position = element.find('.dropdown.positionInViewport');
+
+ if ($position.size()) {
+ piwik.helper.setMarginLeftToBeInViewport($position);
+ }
});
function onClickOutsideElement (event) {
@@ -47,9 +53,9 @@
angular.module('piwikApp').directive('piwikExpandOnHover', piwikExpandOnHover);
- piwikExpandOnHover.$inject = ['$document'];
+ piwikExpandOnHover.$inject = ['$document', 'piwik'];
- function piwikExpandOnHover($document){
+ function piwikExpandOnHover($document, piwik){
return {
restrict: 'A',
@@ -57,6 +63,12 @@
element.on('mouseenter', '.title', function () {
element.addClass('expanded');
+
+ var $position = element.find('.dropdown.positionInViewport');
+
+ if ($position.size()) {
+ piwik.helper.setMarginLeftToBeInViewport($position);
+ }
});
element.on('mouseleave', function () {
diff --git a/plugins/CoreHome/angularjs/selector/selector.directive.less b/plugins/CoreHome/angularjs/selector/selector.directive.less
index 9c0e207d4d..14382fd394 100644
--- a/plugins/CoreHome/angularjs/selector/selector.directive.less
+++ b/plugins/CoreHome/angularjs/selector/selector.directive.less
@@ -54,6 +54,15 @@
&.expanded {
.dropdown {
display: block;
+ margin-top: 2px;
+ margin-left: -1px;
+ position: absolute;
+ z-index: 999;
+ border: 1px solid @theme-color-background-base;
+ border-radius: 2px;
+ box-shadow: 4px 7px 25px rgba(0,0,0,0.3);
+ padding: 16px;
+ background-color: @theme-color-background-contrast;
}
}
}
diff --git a/plugins/CoreHome/angularjs/siteselector/siteselector.controller.js b/plugins/CoreHome/angularjs/siteselector/siteselector.controller.js
index c7ba0656c2..d539bb3328 100644
--- a/plugins/CoreHome/angularjs/siteselector/siteselector.controller.js
+++ b/plugins/CoreHome/angularjs/siteselector/siteselector.controller.js
@@ -14,7 +14,6 @@
$scope.model = siteSelectorModel;
$scope.autocompleteMinSites = AUTOCOMPLETE_MIN_SITES;
- $scope.selectedSite = {id: '', name: ''};
$scope.activeSiteId = piwik.idSite;
$scope.switchSite = function (site) {
diff --git a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html
index f9f135d143..87ecf8c312 100644
--- a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html
+++ b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html
@@ -21,12 +21,27 @@
class="title" tabindex="4">
<span class="icon icon-arrow-bottom"
ng-class="{'iconHidden': model.isLoading, 'collapsed': !view.showSitesList}"></span>
- <span>{{ 'General_Website'| translate }}:
- <span ng-bind-html="selectedSite.name || model.firstSiteName">?</span>
+ <span><span ng-bind-html="selectedSite.name || model.firstSiteName">?</span>
</span>
</a>
<div ng-show="view.showSitesList" class="dropdown">
+
+ <div class="custom_select_search" ng-show="autocompleteMinSites <= model.sites.length || view.searchTerm">
+ <input type="text"
+ piwik-focus-if="view.showSitesList"
+ ng-click="view.searchTerm=''"
+ ng-model="view.searchTerm"
+ ng-change="model.searchSite(view.searchTerm)"
+ placeholder="{{ 'General_Search' | translate }}"
+ class="websiteSearch inp browser-default"/>
+ <img title="Clear"
+ ng-show="view.searchTerm"
+ ng-click="view.searchTerm=''; model.loadInitialSites()"
+ class="reset"
+ src="plugins/CoreHome/images/reset_search.png"/>
+ </div>
+
<div ng-if="allSitesLocation=='top' && showAllSitesItem"
ng-include="'siteselector_allsiteslink.html'"></div>
@@ -51,19 +66,5 @@
<div ng-if="allSitesLocation=='bottom' && showAllSitesItem"
ng-include="'siteselector_allsiteslink.html'"></div>
- <div class="custom_select_search" ng-show="autocompleteMinSites <= model.sites.length || view.searchTerm">
- <input type="text"
- piwik-focus-if="view.showSitesList"
- ng-click="view.searchTerm=''"
- ng-model="view.searchTerm"
- ng-change="model.searchSite(view.searchTerm)"
- placeholder="{{ 'General_Search' | translate }}"
- class="websiteSearch inp"/>
- <img title="Clear"
- ng-show="view.searchTerm"
- ng-click="view.searchTerm=''; model.loadInitialSites()"
- class="reset"
- src="plugins/CoreHome/images/reset_search.png"/>
- </div>
</div>
</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.js b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.js
index 06033fbd30..75dbf1e405 100644
--- a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.js
+++ b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.js
@@ -64,7 +64,10 @@
}
return function (scope, element, attrs, ngModel) {
- scope.selectedSite = {id: attrs.siteid, name: attrs.sitename};
+ if (attrs.siteid && attrs.sitename) {
+ scope.selectedSite = {id: attrs.siteid, name: attrs.sitename};
+ }
+
scope.model.onlySitesWithAdminAccess = scope.onlySitesWithAdminAccess;
if (ngModel) {
@@ -78,6 +81,16 @@
}
});
+ if (ngModel) {
+ ngModel.$render = function() {
+ if (angular.isString(ngModel.$viewValue)) {
+ scope.selectedSite = JSON.parse(ngModel.$viewValue);
+ } else {
+ scope.selectedSite = ngModel.$viewValue;
+ }
+ };
+ }
+
scope.$watch('selectedSite', function (newValue) {
if (ngModel) {
ngModel.$setViewValue(newValue);
diff --git a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.less b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.less
index 78dd32a862..5cb46eea9d 100644
--- a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.less
+++ b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.less
@@ -4,12 +4,15 @@
}
.siteSelector {
a.title {
+ .icon.collapsed {
+ margin-top: -1px;
+ }
.icon.collapsed.iconHidden {
visibility: visible;
}
}
.dropdown {
- max-width: 210px;
+ width: 210px;
}
}
@@ -89,7 +92,6 @@
display: block;
text-decoration: none;
padding-left: 5px;
- margin-left: -5px;
}
.siteSelector .custom_select_ul_list li a:hover,
@@ -99,21 +101,22 @@
.siteSelector .custom_select_all a {
text-decoration: none;
- margin: 0 0 5px -5px;
+ &:hover {
+ text-decoration: none;
+ }
}
.siteSelector .custom_select_search {
- margin: 0;
+ margin: 0 0 8px 0;
height: 33px;
display: block;
white-space: nowrap;
position: relative;
- padding-top: 4px;
.inp {
vertical-align: top;
width: 100%;
- padding: 4px 6px !important;
+ padding: 7px 6px !important;
border: 1px solid #d0d0d0 !important;
background: transparent !important;
font-size: 11px !important;
@@ -121,7 +124,7 @@
}
.reset {
position: absolute;
- top: 10px;
+ top: 8px;
right: 4px;
cursor: pointer;
}
diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
index 32bb13331e..502cdaf723 100644
--- a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
+++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
@@ -14,9 +14,7 @@
</div>
</div>
- <div style="float:left;max-width:730px;">
- <h2 ng-if="selectedWidget.name" class="noTopMargin">{{ selectedWidget.name }}</h2>
-
+ <div class="reportContainer">
<div ng-if="selectedWidget.parameters" class="dimensionReport"
piwik-widget-loader="selectedWidget.parameters"></div>
</div>
diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.less b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.less
new file mode 100644
index 0000000000..c665289d52
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.less
@@ -0,0 +1,64 @@
+.reportsByDimensionView {
+
+ .dimensionCategory {
+ color: @theme-color-text-contrast;
+ }
+
+ .reportContainer {
+ position: absolute;
+ margin-left: 230px;
+ min-width: 500px;
+
+ table.dataTable tr td.label {
+ max-width: 380px;
+ }
+ }
+
+ .entityList {
+
+ ul {
+ li {
+ margin: 4px 0;
+ padding: 0 0 0 16px;
+ line-height: 22px;
+ border-left: 2px solid @theme-color-background-base;
+
+ :hover {
+ opacity: 0.8;
+ }
+ &:hover:not(.activeDimension) {
+ border-color: @theme-color-header-background;
+ border-left-width: 1px;
+ padding-left: 17px;
+ }
+
+ &.activeDimension {
+ border-color: @theme-color-header-background;
+
+ .dimension {
+ font-weight: bold;
+ }
+ }
+
+ .dimension {
+ cursor: pointer;
+ border-bottom: 1px solid #d0d0d0;
+ color: @theme-color-text-contrast;
+ border: 0;
+ }
+ }
+
+ &.listCircle {
+ font-weight: normal;
+ list-style: none;
+ padding: 4px 0;
+ margin-top: 8px;
+ }
+ &.listCircle li a {
+ color: #000;
+ }
+
+ }
+
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
index c378637c71..5533b962ec 100644
--- a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
+++ b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
@@ -26,7 +26,22 @@
scope: {
container: '=piwikWidgetContainer'
},
- templateUrl: 'plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html?cb=' + piwik.cacheBuster
+ templateUrl: 'plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, ngModel) {
+ scope.$watch('container', function (container) {
+ if (container && container.widgets && container.widgets[0] && container.widgets[0].parameters) {
+ var isWidgetized = container.widgets[0].parameters.widget == '1';
+
+ if (isWidgetized) {
+ container.widgets[0].parameters.showtitle = '0';
+ }
+
+ }
+ });
+ }
+ }
};
}
})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
index d1f0cb51f1..7a98b81266 100644
--- a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
+++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
@@ -1,8 +1,9 @@
<div>
-
- <div piwik-activity-indicator loading="loading"/>
+ <div piwik-activity-indicator loading-message="loadingMessage" loading="loading"/>
<div ng-show="loadingFailed">
+ <h2 ng-if="widgetName">{{widgetName}}</h2>
+
<div class="notification system notification-error">
{{ 'General_ErrorRequest'|translate:(''):('') }}
</div>
diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
index b1d49579fc..cb41198e23 100644
--- a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
+++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
@@ -26,12 +26,21 @@
restrict: 'A',
transclude: true,
scope: {
- piwikWidgetLoader: '='
+ piwikWidgetLoader: '=',
+ widgetName: '@'
},
templateUrl: 'plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html?cb=' + piwik.cacheBuster,
compile: function (element, attrs) {
return function (scope, element, attrs, ngModel) {
+ scope.widgetName = attrs.widgetName;
+
+ if (!attrs.widgetName) {
+ scope.loadingMessage = _pk_translate('General_LoadingData');
+ } else {
+ scope.loadingMessage = _pk_translate('General_LoadingPopover', [attrs.widgetName]);
+ }
+
var changeCounter = 0,
currentScope,
currentElement,
@@ -61,9 +70,21 @@
var url = $.param(parameters);
var $urlParams = $location.search();
- $urlParams = angular.copy($urlParams);
- delete $urlParams['category'];
- delete $urlParams['subcategory'];
+
+ if ($.isEmptyObject($urlParams) || !$urlParams || !$urlParams['idSite']) {
+ // happens eg in exported widget etc when URL does not have #?...
+ $urlParams = {idSite: 'idSite', period: 'period',date: 'date'};
+ if (piwikUrl.getSearchParam('widget')) {
+ $urlParams['widget'] = 'widget';
+ }
+ if (piwikUrl.getSearchParam('segment')) {
+ $urlParams['segment'] = 'segment';
+ }
+ } else {
+ $urlParams = angular.copy($urlParams);
+ delete $urlParams['category'];
+ delete $urlParams['subcategory'];
+ }
angular.forEach($urlParams, function (value, key) {
if (!(key in parameters)) {
@@ -71,6 +92,10 @@
}
});
+ if (!parameters || !('showtitle' in parameters)) {
+ url += '&showtitle=1';
+ }
+
url += '&random=' + parseInt(Math.random() * 10000);
return '?' + url;
@@ -102,6 +127,19 @@
scope.loadingFailed = false;
currentElement = contentNode.html(response).children();
+
+ if (scope.widgetName) {
+ var $title = currentElement.find('> .card-content .card-title');
+ if ($title.size()) {
+ $title.text(scope.widgetName);
+ } else {
+ $title = currentElement.find('> h2');
+ if ($title.size()) {
+ $title.text(scope.widgetName);
+ }
+ }
+ }
+
$compile(currentElement)(newScope);
}).error(function () {
diff --git a/plugins/CoreHome/angularjs/widget/widget.directive.html b/plugins/CoreHome/angularjs/widget/widget.directive.html
index d75b273d9d..f0e9c48626 100644
--- a/plugins/CoreHome/angularjs/widget/widget.directive.html
+++ b/plugins/CoreHome/angularjs/widget/widget.directive.html
@@ -1,18 +1,9 @@
<div id="{{ widget.uniqueId }}"
ng-show="view.showWidget"
- class="{{widget.viewDataTable}}"
- ng-class="{'smallTopMargin': (!widget.isFirstInPage && (!widget.name || widgetized))}"
- >
- <!--smallTopMargin: we display small margin if it's not the first widget on the page and if there's no headline -->
- <h2 ng-if="!widget.parameters.widget && widget.name && !widgetized"
- piwik-enriched-headline
- ng-class="{'noTopMargin': widget.isFirstInPage}"
- inline-help="{{widget.documentation}}"
- feature-name="{{widget.name}}">{{widget.name}}</h2>
- <h2 ng-if="widget.parameters.widget && widget.name && !widgetized">{{widget.name}}</h2>
+ ng-class="{'isFirstWidgetInPage': widget.isFirstInPage}">
<div ng-if="!widget.isContainer && widget.parameters"
- piwik-widget-loader="widget.parameters"></div>
+ piwik-widget-loader="widget.parameters" widget-name="{{ widget.name }}"></div>
<div ng-if="widget.isContainer && widget.layout!='ByDimension'">
<div piwik-widget-container="widget"></div>
diff --git a/plugins/CoreHome/javascripts/calendar.js b/plugins/CoreHome/javascripts/calendar.js
index 004c15e0aa..4bd1e7b808 100644
--- a/plugins/CoreHome/javascripts/calendar.js
+++ b/plugins/CoreHome/javascripts/calendar.js
@@ -20,8 +20,88 @@
var currentYear, currentMonth, currentDay, currentDate, currentWeek;
+ function formatDateString(date) {
+ var month = date.getMonth() + 1;
+ var day = date.getDate();
+ if (month < 10) {
+ month = '0' + month;
+ }
+ if (day < 10) {
+ day = '0' + day;
+ }
+
+ return date.getFullYear() + '-' + month + '-' + day;
+ }
+
+ function updateDisplayDate(selectedPeriod, dateText)
+ {
+ piwik.period = selectedPeriod;
+
+ if (dateText && dateText.indexOf(',') > -1) {
+ var dateParts = dateText.split(',');
+ if (dateParts[1]) {
+ piwik.currentDateString = dateParts[1];
+ } else if (dateParts[0]) {
+ piwik.currentDateString = dateParts[0];
+ }
+ } else {
+ piwik.currentDateString = dateText;
+ }
+
+ if (selectedPeriod === 'week') {
+ var millisecondsPerDay = 24 * 60 * 60 * 1000;
+ var currentTimeSelected = currentDate.getTime();
+ // we fix currentDayOfWeek as our week starts on Monday.
+ var currentDayOfWeek = currentDate.getDay();
+ if (currentDayOfWeek === 0) {
+ // Usually Sunday === 0, we set Sunday to the end of the week.
+ currentDayOfWeek = 6;
+ } else {
+ // we move every day one day forward, so Monday which is usually 1 becomes 0
+ currentDayOfWeek = currentDayOfWeek - 1;
+ }
+
+ var firstDayOfWeek = new Date();
+ var lastDayOfWeek = new Date();
+ firstDayOfWeek.setTime(currentTimeSelected - (millisecondsPerDay * currentDayOfWeek));
+ lastDayOfWeek.setTime(firstDayOfWeek.getTime() + (millisecondsPerDay * 6));
+
+ piwik.startDateString = formatDateString(firstDayOfWeek);
+ piwik.endDateString = formatDateString(lastDayOfWeek);
+ } else if (dateText.indexOf(',') !== -1) {
+ var dateParts = dateText.split(',');
+ piwik.startDateString = dateParts[0];
+ piwik.endDateString = dateParts[1];
+ } else {
+ piwik.startDateString = dateText;
+ piwik.endDateString = dateText;
+ }
+
+ var displayDate = dateText;
+ if (selectedPeriod === 'month') {
+ displayDate = _pk_translate('Intl_Month_Long_StandAlone_' + currentMonth) + ' ' + currentYear;
+ } else if (selectedPeriod === 'year') {
+ displayDate = currentYear;
+ } else if (selectedPeriod === 'range' || selectedPeriod === 'week') {
+ displayDate = _pk_translate('General_DateRangeFromTo', [piwik.startDateString, piwik.endDateString]);
+ }
+
+ var $title = $('#date.title');
+ $title.text(displayDate);
+ $title.attr('title', _pk_translate('General_ChooseDate', [piwikHelper.escape(displayDate)]));
+
+ if (typeof initTopControls !== 'undefined' && initTopControls) {
+ initTopControls();
+ }
+ }
+
function setCurrentDate(dateStr) {
- var splitDate = dateStr.split("-");
+ if (dateStr && dateStr.indexOf(',') !== -1) {
+ var parts = dateStr.split(',');
+ dateStr = parts[0];
+ }
+
+ var splitDate = dateStr.split('-');
currentYear = splitDate[0];
currentMonth = splitDate[1] - 1;
currentDay = splitDate[2];
@@ -33,6 +113,7 @@
// eg. Login form
return;
}
+
setCurrentDate(piwik.currentDateString);
var todayDate = new Date;
@@ -40,11 +121,11 @@
var todayYear = todayDate.getFullYear();
var todayDay = todayDate.getDate();
-// min/max date for picker
+ // 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);
-// we start w/ the current period
+ // we start w/ the current period
var selectedPeriod = piwik.period;
function isDateInCurrentPeriod(date) {
@@ -115,6 +196,32 @@
return [true, ''];
}
+ function propagateNewUrlParams(date, period)
+ {
+ if (piwikHelper.isAngularRenderingThePage()) {
+
+ $('#periodString').removeClass('expanded');
+ piwikHelper.hideAjaxLoading('ajaxLoadingCalendar');
+
+ angular.element(document).injector().invoke(function ($location) {
+ var $search = $location.search();
+
+ if (date !== $search.date || period !== $search.period) {
+ // eg when using back button the date might be actually already changed in the URL and we do not
+ // want to change the URL again
+ $search.date = date;
+ $search.period = period;
+ $location.search($search);
+ }
+
+ });
+ } else {
+ // 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=' + period);
+ }
+ }
+
piwik.getBaseDatePickerOptions = function (defaultDate) {
return {
showOtherMonths: false,
@@ -195,6 +302,8 @@
$(function () {
+ var reloading = false;
+
var datepickerElem = $('#datepicker').datepicker(getDatePickerOptions()),
periodLabels = $('#periodString').find('.period-type label'),
periodTooltip = $('#periodString').find('.period-click-tooltip').html();
@@ -250,6 +359,7 @@
var unhighlightAllDates = function () {
// make sure nothing is highlighted
$('.ui-state-active,.ui-state-hover', datepickerElem).removeClass('ui-state-active ui-state-hover');
+ $('.ui-datepicker-current-day', datepickerElem).removeClass('ui-datepicker-current-day');
// color whitespace
if (piwik.period == 'year') {
@@ -269,7 +379,7 @@
// select new dates in calendar
setCurrentDate(dateText);
- piwik.period = selectedPeriod;
+ updateDisplayDate(selectedPeriod, dateText);
// make sure it's called after jquery-ui is done, otherwise everything we do will
// be undone.
@@ -277,9 +387,9 @@
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=' + dateText + '&period=' + selectedPeriod);
+ propagateNewUrlParams(dateText, selectedPeriod);
+ initTopControls();
+ reloading = false;
};
var toggleMonthDropdown = function (disable) {
@@ -353,9 +463,8 @@
}
});
- var reloading = false;
-
var changePeriodWithPageReload = function (periodInput) {
+
var url = periodInput.val(),
period = broadcast.getValueFromUrl('period', url);
@@ -426,7 +535,7 @@
return false;
}
piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
- broadcast.propagateNewPage('period=range&date=' + dateFrom + ',' + dateTo);
+ propagateNewUrlParams(dateFrom + ',' + dateTo, 'range');
})
.show();
@@ -548,6 +657,43 @@
$("#period_id_range").click();
}
+ function updatePeriodPickerFromHash()
+ {
+ var dateHash = broadcast.getValueFromHash('date');
+ var periodHash = broadcast.getValueFromHash('period');
+
+ if (!dateHash || dateHash.length > 30 || !periodHash || periodHash.length > 7) {
+ // invalid data in URL
+ return;
+ }
+
+ if (!/^(\d){4}/.test(dateHash)) {
+ // it's not an actual date, it is 'yesterday' or so meaning date was not changed in calendar
+ return;
+ }
+
+ if (piwik.period === periodHash && piwik.currentDateString === dateHash) {
+ // this period / date is already loaded
+ return;
+ }
+
+ $('input[id=period_id_' + periodHash + ']').click();
+
+ updateDate(dateHash);
+
+ $('input[id=period_id_' + periodHash + ']').click();
+ }
+
+ if (piwikHelper.isAngularRenderingThePage()) {
+ // when using back button etc we need to update the date under circumstances
+ angular.element(document).injector().invoke(function ($rootScope) {
+ $rootScope.$on('$locationChangeSuccess', updatePeriodPickerFromHash);
+ });
+
+ // on initial page load the date/period in hash might be different to the one in the URL
+ updatePeriodPickerFromHash();
+ }
+
initTopControls();
});
diff --git a/plugins/CoreHome/javascripts/corehome.js b/plugins/CoreHome/javascripts/corehome.js
index b0ac486b10..b8951ed4c4 100755
--- a/plugins/CoreHome/javascripts/corehome.js
+++ b/plugins/CoreHome/javascripts/corehome.js
@@ -32,7 +32,7 @@
ajaxRequest.withTokenInUrl();
- var $titleElement = $(this);
+ var $titleElement = headerMessage.find('.title');
$titleElement.addClass('activityIndicator');
ajaxRequest.setCallback(function (response) {
@@ -70,72 +70,21 @@
}
});
- //
- // section toggler behavior
- //
-
- var handleSectionToggle = function (self, showType, doHide) {
- var sectionId = $(self).attr('data-section-id'),
- section = $('#' + sectionId),
- showText = _pk_translate('General_Show'),
- hideText = _pk_translate('General_Hide');
-
- 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'));
- });
-
});
-
-
-
}(jQuery));
+
$( document ).ready(function() {
$('.accessibility-skip-to-content').click(function(e){
$('a[name="main"]').attr('tabindex', -1).focus();
$(window).scrollTo($('a[name="main"]'));
});
+
+ $("nav .activateTopMenu").sideNav({
+ closeOnClick: true,
+ edge: 'right',
+ });
+
+ $('select').material_select();
}); \ No newline at end of file
diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js
index 4386a4bf7b..e1a0693d8c 100644
--- a/plugins/CoreHome/javascripts/dataTable.js
+++ b/plugins/CoreHome/javascripts/dataTable.js
@@ -265,6 +265,12 @@ $.extend(DataTable.prototype, UIControl.prototype, {
dataTableLoaded: function (response, workingDivId) {
var content = $(response);
+ if ($.trim($('.dataTableControls', content).html()) === '') {
+ $('.dataTableControls', content).append('&nbsp;');
+ // fix table controls are not visible because there is no content. prevents limit selection being displayed
+ // in the middle
+ }
+
var idToReplace = workingDivId || $(content).attr('id');
var dataTableSel = $('#' + idToReplace);
@@ -286,6 +292,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
content.trigger('piwik:dataTableLoaded');
piwikHelper.lazyScrollTo(content[0], 400);
+ piwikHelper.compileAngularComponents(content);
return content;
},
@@ -306,7 +313,6 @@ $.extend(DataTable.prototype, UIControl.prototype, {
self.cleanParams();
self.handleSort(domElem);
self.handleLimit(domElem);
- self.handleSearchBox(domElem);
self.handleOffsetInformation(domElem);
self.handleAnnotationsButton(domElem);
self.handleEvolutionAnnotations(domElem);
@@ -314,25 +320,24 @@ $.extend(DataTable.prototype, UIControl.prototype, {
self.applyCosmetics(domElem);
self.handleSubDataTable(domElem);
self.handleConfigurationBox(domElem);
+ self.handleSearchBox(domElem);
self.handleColumnDocumentation(domElem);
self.handleRowActions(domElem);
self.handleCellTooltips(domElem);
self.handleRelatedReports(domElem);
self.handleTriggeredEvents(domElem);
self.handleColumnHighlighting(domElem);
- self.handleExpandFooter(domElem);
self.setFixWidthToMakeEllipsisWork(domElem);
self.handleSummaryRow(domElem);
},
+ isWidgetized: function () {
+ return -1 !== location.search.indexOf('module=Widgetize');
+ },
+
setFixWidthToMakeEllipsisWork: function (domElem) {
var self = this;
- function isWidgetized()
- {
- return -1 !== location.search.indexOf('module=Widgetize');
- }
-
function getTableWidth(domElem)
{
var totalWidth = $(domElem).width();
@@ -351,23 +356,40 @@ $.extend(DataTable.prototype, UIControl.prototype, {
function setMaxTableWidthIfNeeded (domElem, maxTableWidth)
{
+ var $domElem = $(domElem);
+ var dataTableInCard = $domElem.parents('.card').first();
+ var parentDataTable = $domElem.parent('.dataTable');
+
+ dataTableInCard.width('');
+ $domElem.width('');
+ parentDataTable.width('');
+
var tableWidth = getTableWidth(domElem);
if (tableWidth <= maxTableWidth) {
return;
}
- if (isWidgetized() || self.isDashboard()) {
+ if (self.isWidgetized() || self.isDashboard()) {
return;
}
- $(domElem).width(maxTableWidth);
+ if (dataTableInCard && dataTableInCard.length) {
+ // makes sure card has the same width
+ dataTableInCard.width(maxTableWidth);
+ } else {
+ $domElem.width(maxTableWidth);
+ }
- var parentDataTable = $(domElem).parent('.dataTable');
if (parentDataTable && parentDataTable.length) {
// makes sure dataTableWrapper and DataTable has same size => makes sure maxLabelWidth does not get
// applied in getLabelWidth() since they will have the same size.
- parentDataTable.width(maxTableWidth);
+
+ if (dataTableInCard.length) {
+ dataTableInCard.width(maxTableWidth);
+ } else {
+ parentDataTable.width(maxTableWidth);
+ }
}
}
@@ -395,7 +417,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
}
if (labelWidth > maxLabelWidth
- && !isWidgetized()
+ && !self.isWidgetized()
&& innerWidth !== domElem.width()
&& !self.isDashboard()) {
labelWidth = maxLabelWidth; // prevent for instance table in Actions-Pages is not too wide
@@ -425,6 +447,30 @@ $.extend(DataTable.prototype, UIControl.prototype, {
return parseInt(minWidth, 10);
}
+ function getLabelColumnMaxWidth(domElem)
+ {
+ var maxWidth = 0;
+ var maxWidthHead = $('thead .first.label', domElem).css('maxWidth');
+
+ if (maxWidthHead) {
+ maxWidthHead = parseInt(maxWidthHead, 10);
+ if (maxWidthHead > 0) {
+ maxWidth = parseInt(maxWidthHead, 10);
+ }
+ }
+
+ var maxWidthBody = $('tbody tr:nth-child(1) td.label', domElem).css('maxWidth');
+
+ if (maxWidthBody) {
+ maxWidthBody = parseInt(maxWidthBody, 10);
+ if (maxWidthBody && maxWidthBody > 0 && (maxWidth === 0 || maxWidthBody < maxWidth)) {
+ maxWidth = maxWidthBody;
+ }
+ }
+
+ return parseInt(maxWidth, 10);
+ }
+
function removePaddingFromWidth(domElem, labelWidth) {
var maxPaddingLeft = 0;
var maxPaddingRight = 0;
@@ -450,25 +496,115 @@ $.extend(DataTable.prototype, UIControl.prototype, {
return labelWidth;
}
- var minLabelWidth = 125;
- var maxLabelWidth = 440;
-
setMaxTableWidthIfNeeded(domElem, 1200);
- var tableWidth = getTableWidth(domElem);
- var labelColumnMinWidth = getLabelColumnMinWidth(domElem);
- var labelColumnWidth = getLabelWidth(domElem, tableWidth, 125, 440);
- if (labelColumnMinWidth > labelColumnWidth) {
- labelColumnWidth = labelColumnMinWidth;
+ var isTableVisualization = this.jsViewDataTable && this.jsViewDataTable.indexOf('table') !== -1;
+ if (isTableVisualization) {
+ // we do this only for html tables
+
+ var minLabelWidth = 125;
+ var maxLabelWidth = 440;
+
+ var tableWidth = getTableWidth(domElem);
+ var labelColumnMinWidth = getLabelColumnMinWidth(domElem);
+ var labelColumnMaxWidth = getLabelColumnMaxWidth(domElem);
+ var labelColumnWidth = getLabelWidth(domElem, tableWidth, 125, 440);
+
+ if (labelColumnMinWidth > labelColumnWidth) {
+ labelColumnWidth = labelColumnMinWidth;
+ }
+ if (labelColumnMaxWidth && labelColumnMaxWidth < labelColumnWidth) {
+ labelColumnWidth = labelColumnMaxWidth;
+ }
+
+ labelColumnWidth = removePaddingFromWidth(domElem, labelColumnWidth);
+
+ if (labelColumnWidth) {
+ $('td.label', domElem).width(labelColumnWidth);
+ }
+
+ $('td span.label', domElem).each(function () { self.tooltip($(this)); });
+
+ self.overflowContentIfNeeded(domElem);
+ }
+
+ if (!self.windowResizeTableAttached) {
+ self.windowResizeTableAttached = true;
+
+ // on resize of the window we re-calculate everything.
+ var timeout = null;
+ var resizeDataTable = function() {
+
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+
+ timeout = setTimeout(function () {
+ var isInDom = domElem && domElem[0] && document && document.body && document.body.contains(domElem[0]);
+ if (isInDom) {
+ // as domElem might have been removed by now we check whether domElem actually still is in dom
+ // and do this expensive operation only if needed.
+ if (isTableVisualization) {
+ $('td.label', domElem).width('');
+ }
+ self.setFixWidthToMakeEllipsisWork(domElem);
+ } else {
+ $(window).off('resize', resizeDataTable);
+ }
+
+ timeout = null;
+ }, Math.floor((Math.random() * 80) + 220));
+ // we randomize it just a little to not process all dataTables at similar time but to have a little
+ // delay in between for smoother resizing. we want to do it between 300 and 400ms
+ }
+
+ $(window).on('resize', resizeDataTable);
}
+ },
+
+ overflowContentIfNeeded: function (domElem, showScrollbarIfMoreThanThisPxOverlap) {
- labelColumnWidth = removePaddingFromWidth(domElem, labelColumnWidth);
+ var $domNodeToSetOverflow;
- if (labelColumnWidth) {
- $('td.label', domElem).width(labelColumnWidth);
+ if (this.isDashboard()) {
+ $domNodeToSetOverflow = domElem.parents('.widgetContent').first();
+ } else if (this.isWidgetized()) {
+ $domNodeToSetOverflow = domElem.parents('.widget').first();
+ } else {
+ var inReportPage = domElem.parents('.theWidgetContent').first();
+ var displayedAsCard = inReportPage.find('> .card > .card-content');
+ if (displayedAsCard.size()) {
+ $domNodeToSetOverflow = displayedAsCard.first();
+ } else {
+ $domNodeToSetOverflow = inReportPage;
+ }
}
- $('td span.label', domElem).each(function () { self.tooltip($(this)); });
+ if (!$domNodeToSetOverflow || !$domNodeToSetOverflow.size()) {
+ return;
+ }
+
+ // show scrollbars for a report if table does not fit into widget/report page. This happens especially
+ // with AllTableColumn visualization
+ var tableWidth = domElem.width();
+ var dataTableWidth = domElem.find('table.dataTable').width();
+ var widthToCheckElementIsActuallyThere = 10;
+
+ // in dataTables there is a marginLeft -20px and marginRight -20px applied and jquery seems to not consider
+ // this. This results in the actual table always being 40px wider than the domElem. We add another 11px
+ // just in case some calculations are not 100% right
+ var normalOverlapBecauseTableIsFullWidth = showScrollbarIfMoreThanThisPxOverlap || 51;
+ if (tableWidth > widthToCheckElementIsActuallyThere && dataTableWidth > widthToCheckElementIsActuallyThere
+ && (dataTableWidth - tableWidth) > normalOverlapBecauseTableIsFullWidth) {
+ // when after adjusting the columns the widget/report is sitll wider than the actual dataTable, we need
+ // to make it scrollable otherwise reports overlap each other
+
+ $domNodeToSetOverflow.css('overflow-y', 'scroll');
+
+ } else if ($domNodeToSetOverflow.css('overflow-y') === 'scroll') {
+ // undo the overflow as apparently not needed anymore?
+ $domNodeToSetOverflow.css('overflow-y', 'auto');
+ }
},
handleLimit: function (domElem) {
@@ -516,40 +652,32 @@ $.extend(DataTable.prototype, UIControl.prototype, {
}
// setup limit control
- $('.limitSelection', domElem).append('<div><span value="'+ self.param[limitParamName] +'">' + getFilterLimitAsString(self.param[limitParamName]) + '</span></div><ul></ul>');
+
+ var selectionMarkup = '<div class="input-field"><select value="'+ self.param[limitParamName] +'">';
+ var selectedValue = getFilterLimitAsString(self.param[limitParamName]);
if (self.props.show_limit_control) {
- $('.limitSelection ul', domElem).hide();
for (var i = 0; i < numbers.length; i++) {
- $('.limitSelection ul', domElem).append('<li value="' + numbers[i] + '"><span>' + getFilterLimitAsString(numbers[i]) + '</span></li>');
+ var currentValue = getFilterLimitAsString(numbers[i]);
+ var optionSelected = '';
+ if (selectedValue == currentValue) {
+ optionSelected = 'selected';
+ }
+ selectionMarkup += '<option value="' + numbers[i] + '"' + optionSelected + '>' + currentValue + '</option>';
}
- $('.limitSelection ul li:last', domElem).addClass('last');
+ selectionMarkup += '</select></div>';
+
+ $('.limitSelection', domElem).append(selectionMarkup);
+
+ var $limitSelect = $('.limitSelection select', domElem);
if (!self.isEmpty) {
- var show = function() {
- $('.limitSelection ul', domElem).show();
- $('.limitSelection', domElem).addClass('visible');
- $(document).on('mouseup.limitSelection', function(e) {
- if (!$(e.target).closest('.limitSelection').length) {
- 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).closest('li').attr('value'));
- hide();
+ $limitSelect.on('change', function (event) {
+ var limit = $(this).val();
+
if (limit != self.param[limitParamName]) {
setLimitValue(self.param, limit);
- $('.limitSelection>div>span', domElem).text( getFilterLimitAsString(limit)).attr('value', limit);
self.reloadAjaxDataTable();
var data = {};
@@ -559,8 +687,10 @@ $.extend(DataTable.prototype, UIControl.prototype, {
});
}
else {
- $('.limitSelection', domElem).toggleClass('disabled');
+ $limitSelect.toggleClass('disabled');
}
+
+ $limitSelect.material_select();
}
else {
$('.limitSelection', domElem).hide();
@@ -634,69 +764,111 @@ $.extend(DataTable.prototype, UIControl.prototype, {
}
});
- $('.dataTableSearchPattern', domElem)
- .css({display: 'block'})
- .each(function () {
- // when enter is pressed in the input field we submit the form
- $('.searchInput', this)
- .on("keyup",
- function (e) {
- if (isEnterKey(e)) {
- $(this).siblings(':submit').submit();
- }
- }
- )
- .val(currentPattern)
- ;
+ var $searchAction = $('.dataTableAction.searchAction', domElem);
+ if (!$searchAction.size()) {
+ return;
+ }
- $(':submit', this).submit(
- function () {
- var keyword = $(this).siblings('.searchInput').val();
- self.param.filter_offset = 0;
+ $searchAction.on('click', showSearch);
+ $searchAction.find('.icon-close').on('click', hideSearch);
- $.each(patternsToReplace, function (index, pattern) {
- if (0 === keyword.indexOf(pattern.from)) {
- keyword = pattern.to + keyword.substr(1);
- }
- });
+ var $searchInput = $('.dataTableSearchInput', domElem);
- 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;
- }
+ function getOptimalWidthForSearchField() {
+ var controlBarWidth = $('.dataTableControls', domElem).width();
+ var spaceLeft = controlBarWidth - $searchAction.position().left;
+ var idealWidthForSearchBar = 250;
+ var minimalWidthForSearchBar = 150; // if it's only 150 pixel we still show it on same line
+ var width = idealWidthForSearchBar;
+ if (spaceLeft > minimalWidthForSearchBar && spaceLeft < idealWidthForSearchBar) {
+ width = spaceLeft;
+ }
- delete self.param.totalRows;
+ if (width > controlBarWidth) {
+ width = controlBarWidth;
+ }
- self.reloadAjaxDataTable(true, callbackSuccess);
- }
- );
+ return width;
+ }
- $(':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 class="searchReset">\
- <img src="plugins/CoreHome/images/reset_search.png" title="Clear" />\
- </span>')
- .click(function () {
- $('.searchInput', target).val('');
- $(':submit', target).submit();
- });
- $('.searchInput', this).after(clearImg);
+ function hideSearch(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var $searchAction = $(this).parents('.searchAction').first();
+ $searchAction.removeClass('searchActive active forceActionVisible');
+ $searchAction.css('width', '');
+ $searchAction.on('click', showSearch);
+ $searchAction.find('.icon-search').off('click', searchForPattern);
+ $searchInput.val('');
+
+ if (currentPattern) {
+ // we search for this pattern so if there was a search term before, and someone closes the search
+ // we show all results again
+ searchForPattern();
+ }
+ }
+ function showSearch(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var $searchAction = $(this);
+ $searchAction.addClass('searchActive forceActionVisible');
+ var width = getOptimalWidthForSearchField();
+ $searchAction.css('width', width + 'px');
+ $searchAction.find('.dataTableSearchInput').focus();
+
+ $searchAction.find('.icon-search').on('click', searchForPattern);
+ $searchAction.off('click', showSearch);
+ }
+
+ function searchForPattern() {
+ var keyword = $searchInput.val();
+
+ if (!keyword && !currentPattern) {
+ // we search only if a keyword is actually given, or if no keyword is given and a search was performed
+ // before (in this case we want to clear the search basically.)
+ return;
+ }
+
+ self.param.filter_offset = 0;
+
+ $.each(patternsToReplace, function (index, pattern) {
+ if (0 === keyword.indexOf(pattern.from)) {
+ keyword = pattern.to + keyword.substr(1);
}
+ });
+
+ 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;
+ }
+
+ delete self.param.totalRows;
+
+ self.reloadAjaxDataTable(true, callbackSuccess);
+ }
+
+ $searchInput.on("keyup", function (e) {
+ if (isEnterKey(e)) {
+ searchForPattern();
+ } else if (isEscapeKey(e)) {
+ $searchAction.find('.icon-close').click();
+ }
+ });
+
+ if (currentPattern) {
+ $searchInput.val(currentPattern);
+ $searchAction.click();
+ }
if (this.isEmpty && !currentPattern) {
- $('.dataTableSearchPattern', domElem).hide();
+ $searchAction.css({display: 'none'});
}
},
@@ -734,7 +906,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
var totalRows = Number(self.param.totalRows);
if (self.param.keep_summary_row == 1) --totalRows;
if (offsetEnd < totalRows) {
- $(this).css('display', 'inline');
+ $(this).css('visibility', 'visible');
}
});
// bind the click event to trigger the ajax request with the new offset
@@ -751,7 +923,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
$prev.each(function () {
var offset = 1 + Number(self.param.filter_offset);
if (offset != 1) {
- $(this).css('display', 'inline');
+ $(this).css('visibility', 'visible');
}
});
@@ -797,7 +969,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
piwik.annotations.placeEvolutionIcons(annotations, domElem);
// add new section under axis
- annotations.insertAfter($('.datatableRelatedReports', domElem));
+ annotations.insertBefore($('.dataTableFooterNavigation', domElem));
// reposition annotation icons every time the graph is resized
$('.piwik-graph', domElem).on('resizeGraph', function () {
@@ -835,7 +1007,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
undefined, // lastN
function (manager) {
manager.attr('data-is-range', 0);
- $('.annotationView img', domElem)
+ $('.annotationView', domElem)
.attr('title', _pk_translate('Annotations_IconDesc'));
var viewAndAdd = _pk_translate('Annotations_ViewAndAddAnnotations'),
@@ -919,11 +1091,11 @@ $.extend(DataTable.prototype, UIControl.prototype, {
&& annotationManager.attr('data-is-range') == 1) {
if (annotationManager.is(':hidden')) {
annotationManager.slideDown('slow'); // showing
- $('img', this).attr('title', _pk_translate('Annotations_IconDescHideNotes'));
+ $(this).attr('title', _pk_translate('Annotations_IconDescHideNotes'));
}
else {
annotationManager.slideUp('slow'); // hiding
- $('img', this).attr('title', _pk_translate('Annotations_IconDesc'));
+ $(this).attr('title', _pk_translate('Annotations_IconDesc'));
}
}
else {
@@ -941,7 +1113,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
);
// change the tooltip of the view annotation icon
- $('img', this).attr('title', _pk_translate('Annotations_IconDescHideNotes'));
+ $(this).attr('title', _pk_translate('Annotations_IconDescHideNotes'));
}
});
},
@@ -954,15 +1126,6 @@ $.extend(DataTable.prototype, UIControl.prototype, {
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 = self.param.viewDataTable;
@@ -988,50 +1151,6 @@ $.extend(DataTable.prototype, UIControl.prototype, {
self.graphViewStartingThreads = 0;
self.graphViewStartingKeep = false; //show keep flag
- //define collapsed icons
- $('.tableGraphCollapsed a', domElem)
- .each(function (i) {
- if (self.jsViewDataTable == $(this).attr('data-footer-icon-id')) {
- 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) {
- 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');
- }
- });
- $(this).removeClass('tableIconsGroupActive');
- }
- );
-
//handle exportToFormat icons
self.exportToFormat = null;
$('.exportToFormatIcons a', domElem).click(function () {
@@ -1090,6 +1209,11 @@ $.extend(DataTable.prototype, UIControl.prototype, {
var period = self.param.period;
var formatsUseDayNotRange = piwik.config.datatable_export_range_as_day.toLowerCase();
+ if (!format) {
+ // eg export as image has no format
+ return;
+ }
+
if (formatsUseDayNotRange.indexOf(format.toLowerCase()) != -1
&& self.param.period == 'range') {
period = 'day';
@@ -1192,10 +1316,10 @@ $.extend(DataTable.prototype, UIControl.prototype, {
$('.dataTableFlatten', domElem).parent().remove();
}
- var ul = $('div.tableConfiguration ul', domElem);
+ var ul = $('ul.tableConfiguration', domElem);
function hideConfigurationIcon() {
// hide the icon when there are no actions available or we're not in a table view
- $('div.tableConfiguration', domElem).remove();
+ $('.dropdownConfigureIcon', domElem).remove();
}
if (ul.find('li').size() == 0) {
@@ -1203,29 +1327,11 @@ $.extend(DataTable.prototype, UIControl.prototype, {
return;
}
- var icon = $('a.tableConfigurationIcon', domElem);
- icon.click(function () { return false; });
+ var icon = $('a.dropdownConfigureIcon', domElem);
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, setParamCallback) {
return function () {
- close();
if (setParamCallback) {
var data = setParamCallback();
} else {
@@ -1243,7 +1349,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
var getText = function (text, addDefault, replacement) {
if (/(%(.\$)?s+)/g.test(_pk_translate(text))) {
- var values = ['<br /><span class="action">&raquo; '];
+ var values = ['<br /><span class="action">'];
if(replacement) {
values.push(replacement);
}
@@ -1342,7 +1448,6 @@ $.extend(DataTable.prototype, UIControl.prototype, {
if (iconHighlighted) {
icon.addClass('highlighted');
}
- close();
if (!iconHighlighted
&& !(self.param.viewDataTable == 'table'
@@ -1351,21 +1456,6 @@ $.extend(DataTable.prototype, UIControl.prototype, {
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());
- });
- if (width > 0) {
- ul.find('li').width(width);
- }
- close();
- }, 400);
- }
},
// Tell parent widget that the parameters of this table was updated,
@@ -1438,65 +1528,6 @@ $.extend(DataTable.prototype, UIControl.prototype, {
$("tr td", domElem).addClass('column');
},
- handleExpandFooter: function (domElem) {
- var footerIcons = $('.dataTableFooterIcons', domElem);
-
- if (!footerIcons.length) {
- return;
- }
-
- if (this.isWithinDialog(domElem)) {
- $('.dataTableFeatures', domElem).addClass('expanded');
- }
-
- var self = this;
- function toggleFooter(event)
- {
- if (self.isWithinDialog(domElem)) {
- return;
- }
-
- var icons = $('.dataTableFooterIcons', domElem);
- $('.dataTableFeatures', domElem).toggleClass('expanded');
-
- if (event && event.doNotNotifyChange) {
- return;
- }
-
- self.notifyWidgetParametersChange(domElem, {
- isFooterExpandedInDashboard: icons.is(':visible')
- });
- }
-
- var moveNode = $('.datatableFooterMessage', domElem);
- if (!moveNode.length) {
- moveNode = $('.datatableRelatedReports', domElem);
- }
-
- footerIcons.after(moveNode);
-
- $('.expandDataTableFooterDrawer', domElem).after(footerIcons);
-
- var controls = $('.controls', domElem);
- var footerWrap = $('.dataTableFooterWrap', domElem);
- if (controls.length && footerWrap.length) {
- $('.dataTableFooterWrap', domElem).before(controls);
- }
-
- var loadingPiwikBelow = $('.loadingPiwikBelow', domElem);
- if (loadingPiwikBelow.length) {
- loadingPiwikBelow.insertBefore(moveNode);
- }
-
- if (this.param.isFooterExpandedInDashboard) {
- toggleFooter({doNotNotifyChange: true});
- }
-
- var $nodes = $('.foldDataTableFooterDrawer, .expandDataTableFooterDrawer', domElem);
- $nodes.off('click');
- $nodes.on('click', toggleFooter);
- },
-
handleColumnHighlighting: function (domElem) {
var maxWidth = {};
@@ -1839,7 +1870,13 @@ $.extend(DataTable.prototype, UIControl.prototype, {
}
var actionEl = $(document.createElement('a')).attr({href: '#'}).addClass('action' + action.name);
- actionEl.append($(document.createElement('img')).attr({src: action.dataTableIcon}));
+
+ if (action.dataTableIcon.indexOf('icon-') === 0) {
+ actionEl.append($(document.createElement('span')).addClass(action.dataTableIcon + ' rowActionIcon'));
+ } else {
+ actionEl.append($(document.createElement('img')).attr({src: action.dataTableIcon}));
+ }
+
container.append(actionEl);
if (i == availableActionsForReport.length - 1) {
@@ -1903,7 +1940,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
return;
}
- actions.height(tr.innerHeight() - 2);
+ actions.height(tr.innerHeight() - 6);
actions.css('marginLeft', (td.width() + 3 - actions.outerWidth()) + 'px');
},
diff --git a/plugins/CoreHome/javascripts/iframeResizer.min.js b/plugins/CoreHome/javascripts/iframeResizer.min.js
new file mode 100644
index 0000000000..e8f4bcb11e
--- /dev/null
+++ b/plugins/CoreHome/javascripts/iframeResizer.min.js
@@ -0,0 +1,9 @@
+/*! iFrame Resizer (iframeSizer.min.js ) - v3.5.3 - 2016-02-23
+ * Desc: Force cross domain iframes to size to content.
+ * Requires: iframeResizer.contentWindow.min.js to be loaded into the target frame.
+ * Copyright: (c) 2016 David J. Bradshaw - dave@bradshaw.net
+ * License: MIT
+ */
+
+!function(a){"use strict";function b(b,c,d){"addEventListener"in a?b.addEventListener(c,d,!1):"attachEvent"in a&&b.attachEvent("on"+c,d)}function c(b,c,d){"removeEventListener"in a?b.removeEventListener(c,d,!1):"detachEvent"in a&&b.detachEvent("on"+c,d)}function d(){var b,c=["moz","webkit","o","ms"];for(b=0;b<c.length&&!N;b+=1)N=a[c[b]+"RequestAnimationFrame"];N||h("setup","RequestAnimationFrame not supported")}function e(b){var c="Host page: "+b;return a.top!==a.self&&(c=a.parentIFrame&&a.parentIFrame.getId?a.parentIFrame.getId()+": "+b:"Nested host page: "+b),c}function f(a){return K+"["+e(a)+"]"}function g(a){return P[a]?P[a].log:G}function h(a,b){k("log",a,b,g(a))}function i(a,b){k("info",a,b,g(a))}function j(a,b){k("warn",a,b,!0)}function k(b,c,d,e){!0===e&&"object"==typeof a.console&&console[b](f(c),d)}function l(d){function e(){function a(){s(V),p(W)}g("Height"),g("Width"),t(a,V,"init")}function f(){var a=U.substr(L).split(":");return{iframe:P[a[0]].iframe,id:a[0],height:a[1],width:a[2],type:a[3]}}function g(a){var b=Number(P[W]["max"+a]),c=Number(P[W]["min"+a]),d=a.toLowerCase(),e=Number(V[d]);h(W,"Checking "+d+" is in range "+c+"-"+b),c>e&&(e=c,h(W,"Set "+d+" to min value")),e>b&&(e=b,h(W,"Set "+d+" to max value")),V[d]=""+e}function k(){function a(){function a(){var a=0,d=!1;for(h(W,"Checking connection is from allowed list of origins: "+c);a<c.length;a++)if(c[a]===b){d=!0;break}return d}function d(){var a=P[W].remoteHost;return h(W,"Checking connection is from: "+a),b===a}return c.constructor===Array?a():d()}var b=d.origin,c=P[W].checkOrigin;if(c&&""+b!="null"&&!a())throw new Error("Unexpected message received from: "+b+" for "+V.iframe.id+". Message was: "+d.data+". This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.");return!0}function l(){return K===(""+U).substr(0,L)&&U.substr(L).split(":")[0]in P}function w(){var a=V.type in{"true":1,"false":1,undefined:1};return a&&h(W,"Ignoring init message from meta parent page"),a}function y(a){return U.substr(U.indexOf(":")+J+a)}function z(a){h(W,"MessageCallback passed: {iframe: "+V.iframe.id+", message: "+a+"}"),N("messageCallback",{iframe:V.iframe,message:JSON.parse(a)}),h(W,"--")}function A(){var b=document.body.getBoundingClientRect(),c=V.iframe.getBoundingClientRect();return JSON.stringify({iframeHeight:c.height,iframeWidth:c.width,clientHeight:Math.max(document.documentElement.clientHeight,a.innerHeight||0),clientWidth:Math.max(document.documentElement.clientWidth,a.innerWidth||0),offsetTop:parseInt(c.top-b.top,10),offsetLeft:parseInt(c.left-b.left,10),scrollTop:a.pageYOffset,scrollLeft:a.pageXOffset})}function B(a,b){function c(){u("Send Page Info","pageInfo:"+A(),a,b)}x(c,32)}function C(){function d(b,c){function d(){P[g]?B(P[g].iframe,g):e()}["scroll","resize"].forEach(function(e){h(g,b+e+" listener for sendPageInfo"),c(a,e,d)})}function e(){d("Remove ",c)}function f(){d("Add ",b)}var g=W;f(),P[g].stopPageInfo=e}function D(){P[W]&&P[W].stopPageInfo&&(P[W].stopPageInfo(),delete P[W].stopPageInfo)}function E(){var a=!0;return null===V.iframe&&(j(W,"IFrame ("+V.id+") not found"),a=!1),a}function F(a){var b=a.getBoundingClientRect();return o(W),{x:Math.floor(Number(b.left)+Number(M.x)),y:Math.floor(Number(b.top)+Number(M.y))}}function G(b){function c(){M=g,H(),h(W,"--")}function d(){return{x:Number(V.width)+f.x,y:Number(V.height)+f.y}}function e(){a.parentIFrame?a.parentIFrame["scrollTo"+(b?"Offset":"")](g.x,g.y):j(W,"Unable to scroll to requested position, window.parentIFrame not found")}var f=b?F(V.iframe):{x:0,y:0},g=d();h(W,"Reposition requested from iFrame (offset x:"+f.x+" y:"+f.y+")"),a.top!==a.self?e():c()}function H(){!1!==N("scrollCallback",M)?p(W):q()}function I(b){function c(){var a=F(g);h(W,"Moving to in page link (#"+e+") at x: "+a.x+" y: "+a.y),M={x:a.x,y:a.y},H(),h(W,"--")}function d(){a.parentIFrame?a.parentIFrame.moveToAnchor(e):h(W,"In page link #"+e+" not found and window.parentIFrame not found")}var e=b.split("#")[1]||"",f=decodeURIComponent(e),g=document.getElementById(f)||document.getElementsByName(f)[0];g?c():a.top!==a.self?d():h(W,"In page link #"+e+" not found")}function N(a,b){return m(W,a,b)}function O(){switch(P[W].firstRun&&T(),V.type){case"close":n(V.iframe);break;case"message":z(y(6));break;case"scrollTo":G(!1);break;case"scrollToOffset":G(!0);break;case"pageInfo":B(P[W].iframe,W),C();break;case"pageInfoStop":D();break;case"inPageLink":I(y(9));break;case"reset":r(V);break;case"init":e(),N("initCallback",V.iframe),N("resizedCallback",V);break;default:e(),N("resizedCallback",V)}}function Q(a){var b=!0;return P[a]||(b=!1,j(V.type+" No settings for "+a+". Message was: "+U)),b}function S(){for(var a in P)u("iFrame requested init",v(a),document.getElementById(a),a)}function T(){P[W].firstRun=!1}var U=d.data,V={},W=null;"[iFrameResizerChild]Ready"===U?S():l()?(V=f(),W=R=V.id,!w()&&Q(W)&&(h(W,"Received: "+U),E()&&k()&&O())):i(W,"Ignored: "+U)}function m(a,b,c){var d=null,e=null;if(P[a]){if(d=P[a][b],"function"!=typeof d)throw new TypeError(b+" on iFrame["+a+"] is not a function");e=d(c)}return e}function n(a){var b=a.id;h(b,"Removing iFrame: "+b),a.parentNode.removeChild(a),m(b,"closedCallback",b),h(b,"--"),delete P[b]}function o(b){null===M&&(M={x:void 0!==a.pageXOffset?a.pageXOffset:document.documentElement.scrollLeft,y:void 0!==a.pageYOffset?a.pageYOffset:document.documentElement.scrollTop},h(b,"Get page position: "+M.x+","+M.y))}function p(b){null!==M&&(a.scrollTo(M.x,M.y),h(b,"Set page position: "+M.x+","+M.y),q())}function q(){M=null}function r(a){function b(){s(a),u("reset","reset",a.iframe,a.id)}h(a.id,"Size reset requested by "+("init"===a.type?"host page":"iFrame")),o(a.id),t(b,a,"reset")}function s(a){function b(b){a.iframe.style[b]=a[b]+"px",h(a.id,"IFrame ("+e+") "+b+" set to "+a[b]+"px")}function c(b){H||"0"!==a[b]||(H=!0,h(e,"Hidden iFrame detected, creating visibility listener"),y())}function d(a){b(a),c(a)}var e=a.iframe.id;P[e]&&(P[e].sizeHeight&&d("height"),P[e].sizeWidth&&d("width"))}function t(a,b,c){c!==b.type&&N?(h(b.id,"Requesting animation frame"),N(a)):a()}function u(a,b,c,d){function e(){var e=P[d].targetOrigin;h(d,"["+a+"] Sending msg to iframe["+d+"] ("+b+") targetOrigin: "+e),c.contentWindow.postMessage(K+b,e)}function f(){i(d,"["+a+"] IFrame("+d+") not found"),P[d]&&delete P[d]}function g(){c&&"contentWindow"in c&&null!==c.contentWindow?e():f()}d=d||c.id,P[d]&&g()}function v(a){return a+":"+P[a].bodyMarginV1+":"+P[a].sizeWidth+":"+P[a].log+":"+P[a].interval+":"+P[a].enablePublicMethods+":"+P[a].autoResize+":"+P[a].bodyMargin+":"+P[a].heightCalculationMethod+":"+P[a].bodyBackground+":"+P[a].bodyPadding+":"+P[a].tolerance+":"+P[a].inPageLinks+":"+P[a].resizeFrom+":"+P[a].widthCalculationMethod}function w(a,c){function d(){function b(b){1/0!==P[w][b]&&0!==P[w][b]&&(a.style[b]=P[w][b]+"px",h(w,"Set "+b+" = "+P[w][b]+"px"))}function c(a){if(P[w]["min"+a]>P[w]["max"+a])throw new Error("Value for min"+a+" can not be greater than max"+a)}c("Height"),c("Width"),b("maxHeight"),b("minHeight"),b("maxWidth"),b("minWidth")}function e(){var a=c&&c.id||S.id+F++;return null!==document.getElementById(a)&&(a+=F++),a}function f(b){return R=b,""===b&&(a.id=b=e(),G=(c||{}).log,R=b,h(b,"Added missing iframe ID: "+b+" ("+a.src+")")),b}function g(){h(w,"IFrame scrolling "+(P[w].scrolling?"enabled":"disabled")+" for "+w),a.style.overflow=!1===P[w].scrolling?"hidden":"auto",a.scrolling=!1===P[w].scrolling?"no":"yes"}function i(){("number"==typeof P[w].bodyMargin||"0"===P[w].bodyMargin)&&(P[w].bodyMarginV1=P[w].bodyMargin,P[w].bodyMargin=""+P[w].bodyMargin+"px")}function k(){var b=P[w].firstRun,c=P[w].heightCalculationMethod in O;!b&&c&&r({iframe:a,height:0,width:0,type:"init"})}function l(){Function.prototype.bind&&(P[w].iframe.iFrameResizer={close:n.bind(null,P[w].iframe),resize:u.bind(null,"Window resize","resize",P[w].iframe),moveToAnchor:function(a){u("Move to anchor","inPageLink:"+a,P[w].iframe,w)},sendMessage:function(a){a=JSON.stringify(a),u("Send Message","message:"+a,P[w].iframe,w)}})}function m(c){function d(){u("iFrame.onload",c,a),k()}b(a,"load",d),u("init",c,a)}function o(a){if("object"!=typeof a)throw new TypeError("Options is not an object")}function p(a){for(var b in S)S.hasOwnProperty(b)&&(P[w][b]=a.hasOwnProperty(b)?a[b]:S[b])}function q(a){return""===a||"file://"===a?"*":a}function s(b){b=b||{},P[w]={firstRun:!0,iframe:a,remoteHost:a.src.split("/").slice(0,3).join("/")},o(b),p(b),P[w].targetOrigin=!0===P[w].checkOrigin?q(P[w].remoteHost):"*"}function t(){return w in P&&"iFrameResizer"in a}var w=f(a.id);t()?j(w,"Ignored iFrame, already setup."):(s(c),g(),d(),i(),m(v(w)),l())}function x(a,b){null===Q&&(Q=setTimeout(function(){Q=null,a()},b))}function y(){function b(){function a(a){function b(b){return"0px"===P[a].iframe.style[b]}function c(a){return null!==a.offsetParent}c(P[a].iframe)&&(b("height")||b("width"))&&u("Visibility change","resize",P[a].iframe,a)}for(var b in P)a(b)}function c(a){h("window","Mutation observed: "+a[0].target+" "+a[0].type),x(b,16)}function d(){var a=document.querySelector("body"),b={attributes:!0,attributeOldValue:!1,characterData:!0,characterDataOldValue:!1,childList:!0,subtree:!0},d=new e(c);d.observe(a,b)}var e=a.MutationObserver||a.WebKitMutationObserver;e&&d()}function z(a){function b(){B("Window "+a,"resize")}h("window","Trigger event: "+a),x(b,16)}function A(){function a(){B("Tab Visable","resize")}"hidden"!==document.visibilityState&&(h("document","Trigger event: Visiblity change"),x(a,16))}function B(a,b){function c(a){return"parent"===P[a].resizeFrom&&P[a].autoResize&&!P[a].firstRun}for(var d in P)c(d)&&u(a,b,document.getElementById(d),d)}function C(){b(a,"message",l),b(a,"resize",function(){z("resize")}),b(document,"visibilitychange",A),b(document,"-webkit-visibilitychange",A),b(a,"focusin",function(){z("focus")}),b(a,"focus",function(){z("focus")})}function D(){function a(a,c){function d(){if(!c.tagName)throw new TypeError("Object is not a valid DOM element");if("IFRAME"!==c.tagName.toUpperCase())throw new TypeError("Expected <IFRAME> tag, found <"+c.tagName+">")}c&&(d(),w(c,a),b.push(c))}var b;return d(),C(),function(c,d){switch(b=[],typeof d){case"undefined":case"string":Array.prototype.forEach.call(document.querySelectorAll(d||"iframe"),a.bind(void 0,c));break;case"object":a(c,d);break;default:throw new TypeError("Unexpected data type ("+typeof d+")")}return b}}function E(a){a.fn.iFrameResize=function(a){return this.filter("iframe").each(function(b,c){w(c,a)}).end()}}var F=0,G=!1,H=!1,I="message",J=I.length,K="[iFrameSizer]",L=K.length,M=null,N=a.requestAnimationFrame,O={max:1,scroll:1,bodyScroll:1,documentElementScroll:1},P={},Q=null,R="Host Page",S={autoResize:!0,bodyBackground:null,bodyMargin:null,bodyMarginV1:8,bodyPadding:null,checkOrigin:!0,inPageLinks:!1,enablePublicMethods:!0,heightCalculationMethod:"bodyOffset",id:"iFrameResizer",interval:32,log:!1,maxHeight:1/0,maxWidth:1/0,minHeight:0,minWidth:0,resizeFrom:"parent",scrolling:!1,sizeHeight:!0,sizeWidth:!1,tolerance:0,widthCalculationMethod:"scroll",closedCallback:function(){},initCallback:function(){},messageCallback:function(){j("MessageCallback function not defined")},resizedCallback:function(){},scrollCallback:function(){return!0}};a.jQuery&&E(jQuery),"function"==typeof define&&define.amd?define([],D):"object"==typeof module&&"object"==typeof module.exports?module.exports=D():a.iFrameResize=a.iFrameResize||D()}(window||{});
+//# sourceMappingURL=iframeResizer.map \ No newline at end of file
diff --git a/plugins/CoreHome/javascripts/manifest.json b/plugins/CoreHome/javascripts/manifest.json
new file mode 100644
index 0000000000..93a1399678
--- /dev/null
+++ b/plugins/CoreHome/javascripts/manifest.json
@@ -0,0 +1,14 @@
+{
+ "short_name": "Piwik",
+ "name": "Piwik - Open Source Analytics",
+ "icons": [
+ {
+ "src": "../images/favicon.png",
+ "sizes": "32x32",
+ "type": "image/png"
+ }
+ ],
+ "start_url": "../../../",
+ "display": "standalone",
+ "orientation": "portrait"
+} \ No newline at end of file
diff --git a/plugins/CoreHome/javascripts/popover.js b/plugins/CoreHome/javascripts/popover.js
index 28cad1f3f9..2834432e70 100644
--- a/plugins/CoreHome/javascripts/popover.js
+++ b/plugins/CoreHome/javascripts/popover.js
@@ -162,8 +162,11 @@ var Piwik_Popover = (function () {
closeCallback = false;
}
- container[0].innerHTML = ''; // IE8 fix
container.html(html);
+
+ container.children().each(function (i, childNode) {
+ piwikHelper.compileAngularComponents(childNode);
+ })
centerPopover();
},
diff --git a/plugins/CoreHome/javascripts/top_controls.js b/plugins/CoreHome/javascripts/top_controls.js
index dbaa81622f..7c6a707067 100644
--- a/plugins/CoreHome/javascripts/top_controls.js
+++ b/plugins/CoreHome/javascripts/top_controls.js
@@ -23,27 +23,23 @@ function initTopControls() {
return 0;
}
- var $topControlsContainer = $('.top_controls'),
- left = 0;
+ var $topControlsContainer = $('.top_controls');
var allRendered = true;
if ($topControlsContainer.length) {
- $('.piwikTopControl').each(function () {
+ $topControlsContainer.find('.piwikTopControl').each(function () {
var $control = $(this);
if ($control.css('display') == 'none') {
return;
}
- $control.css('left', left);
var width = $control.outerWidth(true);
var isControlFullyRendered = width >= 30;
if (!isControlFullyRendered) {
allRendered = false;
}
-
- left += width;
});
if (allRendered) {
diff --git a/plugins/CoreHome/javascripts/zen-mode.js b/plugins/CoreHome/javascripts/zen-mode.js
new file mode 100644
index 0000000000..13d5015419
--- /dev/null
+++ b/plugins/CoreHome/javascripts/zen-mode.js
@@ -0,0 +1,25 @@
+$(function () {
+
+
+ angular.element(document).injector().invoke(handleZenMode);
+
+ function handleZenMode ($rootElement, $cookies) {
+ var zenMode = !!parseInt($cookies.get('zenMode'), 10);
+
+ function updateZenMode() {
+ if (zenMode) {
+ $('body').addClass('zenMode');
+ } else {
+ $('body').removeClass('zenMode');
+ }
+ }
+
+ Mousetrap.bind('z', function (event) {
+ zenMode = !zenMode;
+ $cookies.put('zenMode', zenMode ? '1' : '0');
+ updateZenMode();
+ });
+
+ updateZenMode();
+ }
+}); \ No newline at end of file
diff --git a/plugins/CoreHome/lang/en.json b/plugins/CoreHome/lang/en.json
index cbc0300282..bfe6056eee 100644
--- a/plugins/CoreHome/lang/en.json
+++ b/plugins/CoreHome/lang/en.json
@@ -1,13 +1,16 @@
{
"CoreHome": {
"CategoryNoData": "No data in this category. Try to \"Include all population\".",
+ "ChangeVisualization": "Change visualization",
"CheckForUpdates": "Check for updates",
"CheckPiwikOut": "Check Piwik out!",
"ClickToEditX": "Click to edit %s",
+ "CloseSearch": "Close search",
"CloseWidgetDirections": "You can close this widget by clicking on the 'X' icon at the top of the widget.",
"DataForThisReportHasBeenPurged": "The data for this report is more than %s months old and has been purged.",
"DataTableExcludeAggregateRows": "Aggregate rows are shown %s Hide them",
"DataTableIncludeAggregateRows": "Aggregate rows are hidden %s Show them",
+ "DataTableHowToSearch": "Press enter or click the search icon to search",
"Default": "default",
"DonateCall1": "Piwik will always cost you nothing to use, but that doesn't mean it costs us nothing to make.",
"DonateCall2": "Piwik needs your continued support to grow and thrive.",
@@ -51,6 +54,13 @@
"UndoPivotBySubtable": "This report has been pivoted %s Undo pivot",
"NoSuchPage": "This page does not exist",
"PivotBySubtable": "This report is not pivoted %1$s Pivot by %2$s",
+ "SystemSummaryWidget": "System Summary",
+ "SystemSummaryNWebsites": "%d websites",
+ "SystemSummaryNSegments": "%d segments",
+ "SystemSummaryNActivatedPlugins": "%d activated plugins",
+ "SystemSummaryPiwikVersion": "Piwik version",
+ "SystemSummaryMysqlVersion": "MySQL version",
+ "SystemSummaryPhpVersion": "PHP version",
"QuickAccessTitle": "Search for %s. Use the arrow keys to navigate through search results. Shortcut: Press 'f' to search.",
"MenuEntries": "Menu entries",
"Segments": "Segments",
diff --git a/plugins/CoreHome/stylesheets/_donate.less b/plugins/CoreHome/stylesheets/_donate.less
index 5a0401a48b..be304efbff 100755
--- a/plugins/CoreHome/stylesheets/_donate.less
+++ b/plugins/CoreHome/stylesheets/_donate.less
@@ -9,13 +9,12 @@
#piwik-worth {
font-size: 1.2em;
font-weight: bold;
- font-style: italic;
display: block;
- margin: 0 1em 0 1em;
+ margin: 0 1em 0 0;
}
.piwik-donate-slider {
- margin: 1em 0 1em 1em;
+ margin: 0 0 1em 1em;
}
.piwik-donate-slider > .slider-range {
@@ -82,7 +81,6 @@
display: inline-block;
margin-left: 1.2em;
font-size: 1em;
- font-style: italic;
}
.piwik-donate-call .donate-submit a.donate-spacer {
@@ -101,7 +99,7 @@
}
.piwik-donate-call > .piwik-donate-message p {
- margin-left: 1em;
+ margin-left: 0;
}
.piwik-donate-call > .form-description {
@@ -110,9 +108,8 @@
.donate-form-instructions {
font-size: .8em;
- margin: 0 1.25em 0 1.25em;
+ margin: 0 1.25em 0 0;
color: @theme-color-text-lighter;
- font-style: italic;
}
.widget .piwik-donate-call {
diff --git a/plugins/CoreHome/stylesheets/cloud.less b/plugins/CoreHome/stylesheets/cloud.less
index 95eca8f303..75969a7f2d 100644
--- a/plugins/CoreHome/stylesheets/cloud.less
+++ b/plugins/CoreHome/stylesheets/cloud.less
@@ -11,7 +11,6 @@
}
.tagCloud {
- padding: 10px;
img {
border: 0;
diff --git a/plugins/CoreHome/stylesheets/coreHome.less b/plugins/CoreHome/stylesheets/coreHome.less
index 00cd0652fe..8f2a0f802c 100644
--- a/plugins/CoreHome/stylesheets/coreHome.less
+++ b/plugins/CoreHome/stylesheets/coreHome.less
@@ -1,15 +1,4 @@
.home {
- h2 {
- font-size: 18px;
- padding: 16px 0 16px 0;
- border: none;
- margin: 0;
- }
-
- h3 {
- font-size: 1.3em;
- margin-top: 2em;
- }
p {
padding-bottom: 1em;
margin-right: 1em;
@@ -17,19 +6,27 @@
}
}
+.card h2 {
+ &.card-title,
+ .card-title {
+ padding: 0 !important;
+ }
+}
+
/* Content */
#content.home {
font-size: 14px;
display: inline-block;
width: 100%;
-
- > h2:first-child {
- padding-top: 7px;
- }
}
#content.admin {
display: inline-block;
+ max-width: 1300px;
+
+ > .row {
+ margin: 0 -0.75rem;
+ }
}
/* 2 columns reports */
@@ -54,7 +51,7 @@
font-size: 14px;
position: absolute;
left: -10000px;
- top: 21px;
+ top: 0;
}
.accessibility-skip-to-content:focus {
display: block;
@@ -91,8 +88,7 @@ div.ui-datepicker {
}
#calendarRangeTo {
- float: left;
- margin-left: 20px;
+ margin-left: 175px;
}
#calendarRangeFrom {
@@ -222,8 +218,16 @@ a.Piwik_Popover_Error_Back {
font-size:18px;
}
-#pluginSettings {
- .settingIntroduction {
- font-weight: bold;
- }
-}
+.systemSummary {
+ .icon {
+ width: 16px;
+ display: inline-block;
+ }
+ .icon-user {
+ font-size: 11px;
+ }
+
+ > div {
+ margin-bottom: 2px;
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/stylesheets/dataTable.less b/plugins/CoreHome/stylesheets/dataTable.less
index d3760872fd..06ad9623f9 100644
--- a/plugins/CoreHome/stylesheets/dataTable.less
+++ b/plugins/CoreHome/stylesheets/dataTable.less
@@ -1,10 +1,7 @@
-@dataTable-link-color: #255792;
-@dataTable-header-background: #e4e2d7;
-@dataTable-headerActive-background: #D5D3C8;
-
@import "dataTable/_dataTable.less";
@import "dataTable/_limitSelection.less";
@import "dataTable/_reportDocumentation.less";
@import "dataTable/_rowActions.less";
@import "dataTable/_subDataTable.less";
-@import "dataTable/_tableConfiguration.less"; \ No newline at end of file
+@import "dataTable/_tableConfiguration.less";
+@import "dataTable/_entityTable.less"; \ No newline at end of file
diff --git a/plugins/CoreHome/stylesheets/dataTable/_dataTable.less b/plugins/CoreHome/stylesheets/dataTable/_dataTable.less
index 861e628f33..ecb1b480f5 100644
--- a/plugins/CoreHome/stylesheets/dataTable/_dataTable.less
+++ b/plugins/CoreHome/stylesheets/dataTable/_dataTable.less
@@ -8,7 +8,6 @@
td .ratio {
color: #999999;
- font-size: 12px;
display: inline-block;
visibility: hidden;
text-align: right;
@@ -21,10 +20,6 @@
}
}
-div.dataTable {
- position:relative;
-}
-
table.dataTable td.label,
table.subDataTable td.label {
width: 100%;
@@ -55,20 +50,53 @@ table.dataTable tr.subDataTable {
text-overflow: ellipsis;
width: inherit;
display: inline-block;
+ vertical-align: text-bottom;
}
}
table.dataTable th {
margin: 0;
- color: @dataTable-link-color;
text-align: left;
padding: 6px 6px 6px 12px;
- background: @dataTable-header-background;
font-size: 12px;
font-weight: normal;
vertical-align: top;
}
+// for dataTables in exported widgets ( we do not want to apply this for widgets in dashboard )
+body>.widget table.dataTable {
+ th {
+ background: @theme-color-background-tinyContrast;
+ }
+}
+
+// for dataTables in report pages (not for widgets in the dashboard)
+.theWidgetContent .card .card-content table.dataTable {
+ margin-left: -20px;
+ width: ~"calc(100% + 40px)";
+
+ th {
+ background: @theme-color-background-tinyContrast;
+
+ &:first-child {
+ padding-left: 20px;
+ }
+ &:last-child {
+ padding-right: 20px;
+ }
+ }
+
+ td {
+ &:first-child {
+ padding-left: 20px;
+ }
+
+ &:last-child {
+ padding-right: 20px;
+ }
+ }
+}
+
table.dataTable th.sortable {
cursor: pointer;
}
@@ -141,15 +169,6 @@ table.dataTable img {
margin-left: 0;
}
-.dataTable > .dataTableWrapper {
- width: 450px;
-}
-
-div.dataTable, div.dataTable > .dataTableWrapper {
- margin-bottom: 9px; // same height as expand/contract button (so datatable height will encompass the
- // expand/contract button)
-}
-
.subDataTable > .dataTableWrapper {
width: 95%;
}
@@ -178,37 +197,80 @@ div.dataTable, div.dataTable > .dataTableWrapper {
font-size: 12px;
}
-.dataTableSearchPattern {
- margin: 5px 0 2px 0;
- height: 20px;
- display: block;
- white-space: nowrap;
- background: url(plugins/Morpheus/images/search_bg.png) no-repeat center 0;
- text-align: center;
+.dataTableSearchInput {
+ display: inline-block;
+ opacity: 0;
+ color: #adadad !important;
+ min-height: 30px !important;
+ padding-left: 25px !important;
+ width: 100% !important;
+ transition: opacity 0.3s ease !important;
+ box-shadow: 0 0 !important;
+ visibility: hidden;
+}
+
+.searchAction:hover {
+ .dataTableSearchInput {
+ &:hover,
+ &:focus {
+ color: #333 !important;
+ }
+ }
}
-.dataTableSearchPattern input {
- vertical-align: top;
- font-size: 10px;
- color: #454545;
- border: 0;
- background: transparent;
- width: 21px;
- height: 17px;
- overflow: hidden;
- opacity: 0;
- filter: Alpha(opacity=0);
+.searchAction {
+ transition: width 0.3s ease !important;
+ text-align: left !important;
+ padding-left: 10px;
+ position: relative;
cursor: pointer;
+
+ .icon-search {
+ display: inline-block;
+ cursor: pointer;
+ z-index: 1;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+
+ .icon-close {
+ display: none;
+ }
}
-.dataTableSearchPattern .searchInput {
- width: 114px;
- height: auto;
- overflow: visible;
- padding: 2px 6px !important;
- opacity: 1;
+.searchAction.searchActive {
cursor: text;
- filter: Alpha(opacity=100);
+
+ .dataTableSearchInput {
+ visibility: visible;
+ }
+
+ .icon-close {
+ display: inline-block;
+ font-size: 10px;
+ cursor: pointer;
+ position: absolute;
+ top: 12px;
+ right: 10px;
+ color: #adadad !important;
+ &:hover,
+ &:focus {
+ color: #333 !important;
+ }
+ }
+ .icon-search {
+ cursor: pointer;
+ color: #adadad !important;
+ &:hover,
+ &:focus {
+ color: #333 !important;
+ }
+ }
+
+ .dataTableSearchInput {
+ opacity: 1;
+ }
}
.dataTableNext,
@@ -216,44 +278,43 @@ div.dataTable, div.dataTable > .dataTableWrapper {
font-size: 12px;
color: #184A83;
cursor: pointer;
+ &:hover {
+ text-decoration: underline;
+ }
}
.datatableRelatedReports {
color: #888;
- font-size: 11px;
+ font-size: 12px;
padding-bottom: 5px;
margin-top: 6px;
}
-#dashboard .datatableRelatedReports {
- margin-top: 0px;
-}
-
.datatableRelatedReports span {
cursor: pointer;
font-weight: bold;
+ &:hover {
+ text-decoration: underline;
+ }
}
.dataTableFeatures {
text-align: center;
}
-.dataTableFooterNavigation {
- padding: 5px 0;
-}
-
.dataTableNext,
-.dataTablePrevious,
-.dataTableSearchPattern {
- display: none;
+.dataTablePrevious {
+ visibility: hidden;
}
-.dataTableFeatures .loadingPiwik {
- font-size: 0.9em;
+.dataTablePaginationControl {
+ margin: 5px 0 !important;
}
-.subDataTable .dataTableFooterIcons {
- height: 0;
+.dataTableFeatures .loadingPiwik {
+ font-size: 13px;
+ display: inline-block;
+ padding-bottom: 13px;
}
.dataTable .loadingPiwikBelow {
@@ -262,140 +323,21 @@ div.dataTable, div.dataTable > .dataTableWrapper {
text-align: center;
}
-.dataTableFooterIcons div {
- padding-bottom: 4px;
-}
-
.dataTableFeatures {
- &.expanded {
- .dataTableFooterIcons {
- display: block;
- }
-
- .expandDataTableFooterDrawer {
- display: none;
- }
- }
-
&.hasEvolution {
.dataTableFooterIcons {
margin-top: 17px;
}
- .expandDataTableFooterDrawer {
- margin-top: 20px;
- }
}
-
- .expandDataTableFooterDrawer {
- display: block;
- margin-top: 5px;
- margin-bottom: -1px;
- margin-left: auto;
- margin-right: auto;
- background-color: @theme-color-widget-background;
- border: 1px solid @theme-color-background-tinyContrast;
- height: 9px;
- width: 70px;
- -webkit-border-bottom-left-radius: 10px;
- -webkit-border-bottom-right-radius: 10px;
- -moz-border-radius-bottomleft: 10px;
- -moz-border-radius-bottomright: 10px;
- border-bottom-left-radius: 10px;
- border-bottom-right-radius: 10px;
- line-height: 0px;
-
- img {
- margin-bottom: 0px;
- line-height: 0px;
- }
- }
-}
-
-.dataTableFeatures {
- border-bottom: 1px solid @theme-color-background-tinyContrast;
}
.widget .dataTableFeatures {
border-bottom-style: none !important;
}
-.dataTableFooterIcons {
-
- display: none;
- margin-top: 5px;
- height: auto;
-
- div {
- padding-bottom: 2px;
- }
-
- .foldDataTableFooterDrawer {
- display: block;
- padding-bottom: 0px;
- margin-bottom: -1px;
- margin-top: 0px;
- margin-left: auto;
- margin-right: auto;
- background-color: @theme-color-widget-background;
- border: 1px solid @theme-color-background-tinyContrast;
- -webkit-border-top-left-radius: 10px;
- -webkit-border-top-right-radius: 10px;
- -moz-border-radius-topleft: 10px;
- -moz-border-radius-topright: 10px;
- border-top-left-radius: 10px;
- border-top-right-radius: 10px;
- line-height: 0px;
- height: 9px;
- width: 70px;
- clear: both;
-
- img {
- margin-top: 2px;
- margin-bottom: 0px;
- line-height: 0px;
- }
- }
-
- .controls {
- clear: left;
- padding: 0px 0px 15px;
- text-align: left;
- color: #333;
- }
-}
-
-#dashboard .dataTableFeatures,.widget .dataTableFeatures {
- .expandDataTableFooterDrawer {
- -webkit-border-top-left-radius: 10px;
- -webkit-border-top-right-radius: 10px;
- -moz-border-radius-topleft: 10px;
- -moz-border-radius-topright: 10px;
- border-top-left-radius: 10px;
- border-top-right-radius: 10px;
-
- -webkit-border-bottom-left-radius: 0px;
- -webkit-border-bottom-right-radius: 0px;
- -moz-border-radius-bottomleft: 0px;
- -moz-border-radius-bottomright: 0px;
- border-bottom-left-radius: 0px;
- border-bottom-right-radius: 0px;
- }
-}
-
-.dataTableFooterIcons .foldDataTableFooterDrawer,
-.dataTableFeatures .expandDataTableFooterDrawer {
- cursor: pointer;
-}
-
-.widget .foldDataTableFooterDrawer {
- margin-bottom: -9px;
-}
// customizing footer appearance for row evolution graph
.ui-dialog .dataTableVizEvolution {
- .expandDataTableFooterDrawer,.foldDataTableFooterDrawer {
- display: none !important;
- }
.dataTableFooterIcons {
display: block;
@@ -407,29 +349,10 @@ div.dataTable, div.dataTable > .dataTableWrapper {
}
}
-@-moz-document url-prefix() {
-
- #dashboard .dataTableFeatures .expandDataTableFooterDrawer {
- line-height: 1px;
-
- img {
- margin-bottom: 1px;
- line-height: 1px;
- }
- }
-}
-
-.dataTableFooterIcons {
- min-height: 26px;
- white-space: nowrap;
- font-size: 10px;
- padding: 6px 5px 0px;
- border-top: 1px solid #B6B0A6;
-}
-
.dataTableFooterWrap {
position: relative;
float: left;
+ margin-left: 10px;
}
.dataTableFooterWrap select {
@@ -437,23 +360,6 @@ div.dataTable, div.dataTable > .dataTableWrapper {
margin: 1px 0 1px 10px;
}
-.tableIcon {
- background: #f2f1ed;
- display: inline-block;
- float: left;
- margin: 0 1px 0 0;
- padding: 2px;
- border-radius: 2px;
-}
-
-.tableIcon:hover {
- background: #e9e8e1;
-}
-
-.activeIcon {
- background: #e9e8e1;
-}
-
.tableIconsGroup > span > span {
position:relative;
float:left;
@@ -465,14 +371,6 @@ div.dataTable, div.dataTable > .dataTableWrapper {
left: 0;
}
-.exportToFormatItems {
- background: #dcdacf;
- float: left;
- margin: 0 1px 0 0;
- padding: 4px 6px 3px 6px;
- color: #968d7f;
- border-radius: 2px;
-}
.exportToFormatItems img {
vertical-align: middle;
@@ -508,17 +406,6 @@ div.dataTable, div.dataTable > .dataTableWrapper {
background: #e9e8e1;
}
-.exportToFormatIcons,
-.dataTableFooterIconsShow {
- float: left;
-}
-
-.dataTableFooterIcons,
-.dataTableFooterIcons a {
- text-decoration: none;
- color: @dataTable-link-color;
-}
-
.dataTableSpacer {
clear: both;
}
@@ -534,60 +421,71 @@ div.dataTable, div.dataTable > .dataTableWrapper {
}
tr.level0 td.label {
- padding-left: 1.5em;
+ padding-left: 20px !important;
+}
+
+.widget {
+ tr.level0 td.label {
+ padding-left: 12px !important;
+ }
}
tr.level1 td.label {
- padding-left: 2.5em;
+ padding-left: 2.5em !important;
}
tr.level2 td.label {
- padding-left: 3.5em;
+ padding-left: 3.5em !important;
}
tr.level3 td.label {
- padding-left: 4.5em;
+ padding-left: 4.5em !important;
}
tr.level4 td.label {
- padding-left: 5em;
+ padding-left: 5em !important;
}
tr.level5 td.label {
- padding-left: 5.5em;
+ padding-left: 5.5em !important;
}
tr.level6 td.label {
- padding-left: 6em;
+ padding-left: 6em !important;
}
tr.level7 td.label {
- padding-left: 6.5em;
+ padding-left: 6.5em !important;
}
tr.level8 td.label {
- padding-left: 7em;
+ padding-left: 7em !important;
}
tr.level9 td.label {
- padding-left: 7.5em;
+ padding-left: 7.5em !important;
}
tr.level10 td.label {
- padding-left: 8em;
+ padding-left: 8em !important;
}
tr.level11 td.label {
- padding-left: 8.5em;
+ padding-left: 8.5em !important;
}
tr.level12 td.label {
- padding-left: 9em;
+ padding-left: 9em !important;
}
/* less right margins for the link image in the Pa*/
.dataTableActions table.dataTable img.link {
margin-right: 0.5em;
- margin-left: -0.5em;
- margin-top: -8px;
+ margin-left: 0;
+ margin-bottom: 4px;
+ vertical-align: text-bottom;
+}
+
+table.dataTable td.label img {
+ margin-top: -3px;
}
tr td.label img.plusMinus {
- margin-left: -10px;
+ margin-left: -3px;
margin-right: 3px;
- margin-top: -5px;
+ margin-top: -3px;
}
.pk-emptyDataTable {
@@ -597,7 +495,7 @@ tr td.label img.plusMinus {
}
.widget .pk-emptyDataTable {
- padding-left: 0px;
+ padding-left: 0;
padding-top: 33px;
text-align: center;
}
@@ -621,7 +519,6 @@ table.dataTable span.cell-tooltip {
}
.dataTable .jqplot-graph {
- padding-left: 6px;
> div {
position: relative;
}
@@ -642,3 +539,72 @@ td.cellSubDataTable .loadingPiwik {
display: inline;
}
}
+
+.dataTableControls {
+ text-align: left;
+ position: relative;
+ padding-left: 0 !important;
+
+ &.col {
+ padding-right: 0;
+ }
+
+ .dropdown-content {
+ a {
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ }
+
+ .dataTableAction {
+ border: 0;
+ border: none;
+ border-radius: 2px;
+ display: inline-block;
+ height: 36px;
+ line-height: 36px;
+ outline: 0;
+ width: 36px;
+ text-transform: uppercase;
+ vertical-align: middle;
+ text-decoration: none !important;
+ text-align: center;
+ color: #adadad;
+ font-size: 16px;
+ &:hover {
+ color: #333;
+ background-color: @theme-color-background-base;
+ text-decoration: none !important;
+ }
+
+ .icon-export {
+ margin-top: 6px;
+ display: block;
+ }
+
+ .icon-more-verti {
+ font-size: 18px;
+ }
+ }
+}
+
+@media only screen and (min-width: 993px) {
+ #dashboardWidgetsArea .widget,
+ .theWidgetContent > div:not(#dashboard) {
+ &:hover {
+ .limitSelection,
+ .dataTableControls .dataTableAction {
+ visibility: visible;
+ }
+ }
+ .limitSelection,
+ .dataTableControls .dataTableAction {
+ visibility: hidden;
+
+ &.forceActionVisible {
+ visibility: visible;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/stylesheets/dataTable/_entityTable.less b/plugins/CoreHome/stylesheets/dataTable/_entityTable.less
new file mode 100644
index 0000000000..92957c2040
--- /dev/null
+++ b/plugins/CoreHome/stylesheets/dataTable/_entityTable.less
@@ -0,0 +1,122 @@
+/* main data table */
+table.entityTable {
+
+ thead {
+ tr {
+ th {
+ &:first-child {
+ padding-left: 20px !important;
+ }
+
+ text-transform: none;
+ background-color: @theme-color-background-tinyContrast;
+ vertical-align: middle !important;
+ text-align: left !important;
+ font-size: 15px;
+ padding: 16px 10px;
+ white-space: nowrap;
+ padding: 22px 16px !important;
+ margin: 0;
+ font-weight: normal;
+ border-radius: 0;
+ color: @theme-color-text;
+ }
+ }
+ }
+
+ tbody {
+ tr {
+ &:hover {
+ td {
+ background-color: @theme-color-background-tinyContrast;
+ }
+ }
+ &.highlighted td {
+ background-color: #ECF9DD;
+ }
+
+ td {
+ &:first-child {
+ padding-left: 20px !important;
+ }
+
+ color: @theme-color-text;
+ font-size: 13px;
+ line-height: 16px;
+ background-color: @theme-color-background-contrast;
+ vertical-align: top !important;
+ padding: 16px;
+ border-bottom: 1px solid @theme-color-background-tinyContrast !important;
+ border-radius: 0;
+
+ a {
+ color: @theme-color-link;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline !important;
+ }
+ }
+
+ }
+ }
+ }
+}
+
+// needed for IE10
+.card button.table-action {
+ background-color: @theme-color-background-contrast !important;
+ &:hover {
+ background-color: @theme-color-background-base !important;
+ }
+}
+.table-action {
+ padding: 16px;
+ height: auto;
+ margin: 0;
+ line-height: 1;
+ background-color: inherit;
+ border: 0;
+ color: inherit !important;
+ display: inline-block;
+ &:hover {
+ background-color: @theme-color-background-base !important;
+ box-shadow: 0 1px 2px 0 rgba(0,0,0,0.16), 0 1px 5px 0 rgba(0,0,0,0.12);
+ }
+}
+
+.card .tableActionBar {
+ a, button {
+ background-color: @theme-color-background-contrast;
+ &:focus {
+ background-color: @theme-color-background-contrast;
+ }
+ }
+}
+
+.tableActionBar {
+ a,
+ button {
+ height: 54px;
+ line-height: 54px;
+ box-shadow: none;
+ background-color: rgba(0, 0, 0, 0);
+ color: @theme-color-text !important;
+ cursor: pointer;
+ text-decoration: none;
+ background-color: inherit;
+ text-align: center;
+ letter-spacing: .5px;
+ transition: .2s ease-out;
+ border: none;
+ border-radius: 2px;
+ display: inline-block;
+ padding: 0 2rem;
+ text-transform: uppercase;
+ vertical-align: middle;
+ -webkit-tap-highlight-color: transparent;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/stylesheets/dataTable/_limitSelection.less b/plugins/CoreHome/stylesheets/dataTable/_limitSelection.less
index 44a6ea1243..ab678b427a 100644
--- a/plugins/CoreHome/stylesheets/dataTable/_limitSelection.less
+++ b/plugins/CoreHome/stylesheets/dataTable/_limitSelection.less
@@ -1,9 +1,12 @@
.limitSelection {
- float: right;
+ text-align: right;
position: relative;
margin-left: 5px;
min-height: 20px;
- z-index: 1;
+ padding-top: 4px;
+ vertical-align: top;
+ padding-right: 0 !important;
+ padding-left: 0 !important;
}
.limitSelection.hidden {
@@ -11,14 +14,60 @@
}
.limitSelection > div {
- border: 1px solid #DFDFDF;
- border-radius: 4px;
- background: url(plugins/Morpheus/images/sort_subtable_desc_light.png) no-repeat right 2px;
- padding: 0 14px 0 4px;
- display: block;
- width: 41px;
- height: 26px;
- cursor: pointer;
+ .select-wrapper:hover {
+ background-color: #edecec;
+ border-radius: 2px;
+ color: #333;
+ cursor: pointer;
+
+ input.select-dropdown {
+ color: #333;
+ }
+ span.caret {
+ color: #333 !important;
+ }
+ }
+
+ .select-wrapper {
+ input.select-dropdown {
+ height: 36px;
+ line-height: 30px;
+ font-size: 16px;
+ margin-bottom: 0;
+ padding-left: 5px;
+ width: 42px !important;
+ overflow: hidden;
+ border: 0;
+ transition: all 0s;
+ color: #adadad;
+ &:hover {
+ color: #333;
+ border-color: #333;
+ }
+ }
+ span.caret {
+ top: 11px;
+ right: 4px;
+ color: #adadad;
+ }
+ }
+
+ margin-top: 0;
+ display: inline-block;
+ width: 47px;
+ height: 36px;
+}
+
+.widgetpreview-preview,
+#dashboardWidgetsArea {
+ .limitSelection .select-wrapper span.caret {
+ top: 9px;
+ }
+}
+body > .widget {
+ .limitSelection .select-wrapper span.caret {
+ top: 9px;
+ }
}
.limitSelection.disabled > div {
@@ -28,40 +77,33 @@
}
.limitSelection.visible > div {
- border-radius: 0 0 4px 4px;
background-image: url(plugins/Morpheus/images/sort_subtable_asc_light.png)
}
.limitSelection > ul {
margin-top: 1px;
overflow: visible;
- background-color: @theme-color-background-base;
}
.limitSelection > ul > li {
cursor: pointer;
padding: 0 10px 0 4px;
- font-size: 1.1em;
+ font-size: 1em;
font-weight: bold;
- height: 20px;
- margin-top: -40px;
- background-color: @theme-color-background-base;
- border-left: 1px solid #DFDFDF;
- border-right: 1px solid #DFDFDF;
+ height: 24px;
+ margin-top: -48px;
+ background-color: @theme-color-background-contrast;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
vertical-align: middle;
text-align: right;
}
.limitSelection > ul > li.last {
- border-top: 1px solid #DFDFDF;
- border-radius: 4px 4px 0 0;
+ border-top: 1px solid #ccc;
}
.limitSelection > ul > li:hover {
background-color: @color-silver-l95;
}
-.limitSelection span {
- padding-top: 3px;
- display: inline-block;
-}
diff --git a/plugins/CoreHome/stylesheets/dataTable/_reportDocumentation.less b/plugins/CoreHome/stylesheets/dataTable/_reportDocumentation.less
index 6d071025f9..eee9175bfe 100644
--- a/plugins/CoreHome/stylesheets/dataTable/_reportDocumentation.less
+++ b/plugins/CoreHome/stylesheets/dataTable/_reportDocumentation.less
@@ -29,9 +29,8 @@ table.dataTable th .columnDocumentationTitle {
font-weight: normal;
border: 1px solid #e4e5e4;
margin: 0 0 10px 0;
- padding: 0;
- border-radius: 4px;
- max-width: 500px;
+ padding: 4px 4px 4px 0;
+ border-radius: 2px;
}
.reportDocumentation p {
diff --git a/plugins/CoreHome/stylesheets/dataTable/_rowActions.less b/plugins/CoreHome/stylesheets/dataTable/_rowActions.less
index eb0a8a4301..c13b4c6493 100644
--- a/plugins/CoreHome/stylesheets/dataTable/_rowActions.less
+++ b/plugins/CoreHome/stylesheets/dataTable/_rowActions.less
@@ -8,15 +8,19 @@ table.dataTable .dataTableRowActions {
z-index: 1000; /* Work around FF bug to make sure it displays over ellipsis */
}
-*+html table.dataTable .dataTableRowActions {
- margin-top: -7px;
-}
-
table.dataTable .dataTableRowActions a {
display: block;
float: left;
padding: 6px 4px 6px 0;
margin: 0;
+
+ .rowActionIcon {
+ font-size: 19px;
+ color: @color-gray;
+ &:hover {
+ color: @theme-color-text;
+ }
+ }
}
table.dataTable .dataTableRowActions a.leftmost {
diff --git a/plugins/CoreHome/stylesheets/dataTable/_tableConfiguration.less b/plugins/CoreHome/stylesheets/dataTable/_tableConfiguration.less
index f63e2eb573..383646e7d7 100644
--- a/plugins/CoreHome/stylesheets/dataTable/_tableConfiguration.less
+++ b/plugins/CoreHome/stylesheets/dataTable/_tableConfiguration.less
@@ -1,74 +1,15 @@
-.tableConfiguration {
- float: right;
- position: relative;
- margin-left: 5px;
- min-height: 20px;
- min-width: 25px;
+a.dropdownConfigureIcon.highlighted {
+ color: @theme-color-brand;
}
-a.tableConfigurationIcon {
- display: block;
- width: 30px;
- height: 22px;
- background: url(plugins/Morpheus/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(plugins/Morpheus/images/configure-highlight.png);
- position: absolute;
- z-index: 9;
- right: 0;
-}
-
-.tableConfiguration ul {
- overflow: visible;
- display: none;
- position: relative;
- z-index: 8;
- text-align: left;
-}
.tableConfiguration ul.open {
display: block;
}
-.tableConfiguration ul li {
- padding: 0;
- font-size: 1.1em;
- height: 40px;
- margin-top: -80px;
- background-color: @theme-color-background-base;
- border: 1px solid #DFDFDF;
- border-width: 0 1px;
- vertical-align: middle;
-}
-
-.tableConfiguration ul li.firstDummy {
- border-bottom-width: 1px;
- border-radius: 0 0 4px 4px;
- height: 25px;
- cursor: default;
- margin-top: -4px;
-}
-
-.tableConfiguration ul li.first {
- margin-top: -65px;
-}
-
-.tableConfiguration ul li.last {
- border-top-width: 1px;
- border-radius: 4px 4px 0 0;
-}
-
.tableConfiguration div.configItem {
cursor: pointer;
- padding: 5px 10px;
+ padding: 16px;
line-height: 15px;
color: @theme-color-text-light;
}
@@ -78,5 +19,6 @@ a.tableConfigurationIcon.highlighted {
}
.tableConfiguration div.configItem span.action {
- color: @dataTable-link-color;
+ padding-top: 8px;
+ display: inline-block;
}
diff --git a/plugins/CoreHome/stylesheets/layout.less b/plugins/CoreHome/stylesheets/layout.less
index 03ef1c2959..d87a283721 100644
--- a/plugins/CoreHome/stylesheets/layout.less
+++ b/plugins/CoreHome/stylesheets/layout.less
@@ -1,58 +1,54 @@
- #header {
- padding: 0 15px;
- background-color: @theme-color-background-base;
- height: 50px;
-
- /* Clear fix */
- &:after {
- display: table;
- clear: both;
- content: "";
- }
+.card-content > .row:last-child {
+ margin-bottom: 0;
+}
- .icon-menu-hamburger {
- display: none;
- }
+nav {
+ background-color: @theme-color-header-background !important;
- #logo {
+ .brand-logo {
+ font-size: 28px;
}
- #topRightBar {
- li {
- display: inline-block;
- }
-
- .topBarElem {
- color: @theme-color-text;
- text-decoration: none;
- padding: 14px 12px;
- display: inline-block;
- height: 100%;
- transition: background-color 150ms linear;
+ .activateLeftMenu {
+ float: left;
+ }
- &:hover, &:focus {
- background-color: @theme-color-menu-contrast-background;
- }
+ .activateTopMenu {
+ float: right;
+ }
- &.active {
- color: @theme-color-brand;
- }
+ .icon-more-verti,
+ .icon-menu-hamburger {
+ font-size: 2.7rem;
+ padding: 8px 10px 10px;
+ display: inline-block;
+ cursor: pointer;
+ }
- .menuDropdown {
- color: @theme-color-text;
- .title:after {
- color: @theme-color-text;
- border-top-color: @theme-color-text;
+ .side-nav {
+ .languageSelection {
+ display: block !important;
+ color: #444;
+ padding: 0 30px;
+ .items {
+ margin-left: 0 !important;
+ .item {
+ height: auto;
}
}
}
+ }
- .navbar-right {
- height: 48px;
- position: absolute;
- right: 10px;
- font-size: 13px;
+ .languageSelection {
+ &:hover {
+ background-color: rgba(0,0,0,0.1);
+ }
+ }
+
+ ul a {
+ &:hover {
+ text-decoration: none;
}
}
}
@@ -92,8 +88,8 @@
#root {
.pageWrap {
- padding-left: 18px;
- padding-right: 15px;
+ padding-left: 19px;
+ padding-right: 16px;
}
#secondNavBar + .pageWrap {
@@ -105,58 +101,13 @@
margin-right:15px;
}
- @media all and (max-width: 749px) {
+ @media all and (max-width: 992px) {
.pageWrap {
margin-left: 0 !important;
}
+ }
- #header {
- min-height: 50px;
- height: auto;
- }
-
- #topRightBar {
- .navbar-right {
- text-align: left;
- margin-left: 2px;
- display: inline-block;
- position: relative;
- height: auto;
- min-height: 48px;
- }
- }
-
- #navbar-collapse1 {
- margin-left: 45px;
- }
-
- #secondNavBar:not(.standalone) {
- z-index: 9999;
- position: absolute;
- border-right: 1px solid @theme-color-background-tinyContrast;
- border-top: 1px solid @theme-color-background-tinyContrast;
- -webkit-box-shadow: 4px 4px 18px 1px rgba(0,0,0,0.75);
- -moz-box-shadow: 4px 4px 18px 1px rgba(0,0,0,0.75);
- box-shadow: 4px 4px 18px 1px rgba(0,0,0,0.75);
- -webkit-animation-duration: 0.3s;
- animation-duration: 0.3s;
- -webkit-animation-fill-mode: both;
- animation-fill-mode: both;
- -webkit-animation-timing-function: linear;
- animation-timing-function: linear;
-
- display: none;
- &.open {
- display: block;
- -webkit-animation-name: fadeInLeft;
- animation-name: fadeInLeft;
- }
-
- #search {
- display: none;
- }
- }
-
+ @media all and (max-width: 600px) {
.top_controls {
height: auto;
@@ -164,28 +115,27 @@
.isPiwikDemo,
.piwikTopControl {
position: static !important;
- margin: 0 0 10px;
float: none;
- display: inline-block;
+ margin: 0 23px 10px 20px !important;
+ display: block !important;
}
- .isPiwikDemo {
- margin-top: 8px;
+ .piwikSelector {
+ display: block;
}
- }
- .icon-menu-hamburger {
- padding: 10px 10px 10px;
- display: inline-block;
- float: left;
- cursor: pointer;
- font-size: 23px;
- }
+ .quick-access {
+ width: auto !important;
- #logo {
- display: none;
- }
+ .icon-search {
+ left: 30px;
+ }
+ }
+ .isPiwikDemo {
+ margin-top: 8px;
+ }
+ }
}
#ajaxLoadingDiv {
@@ -195,26 +145,11 @@
#secondNavBar {
width: 224px;
float:left;
-
- #search {
- padding: 8px 0 13px 8px;
- height: 61px;
-
- .quick-access {
- z-index: 1000;
- position: absolute;
- width: 216px;
- input {
- height: 33px;
- font-size: 11px;
- padding: 10px 12px 10px 10px;
- }
- }
- }
+ background-color: @theme-color-menu-contrast-background;
.navbar {
- background-color: @theme-color-menu-contrast-background;
- border-bottom: 1px solid @theme-color-menu-contrast-background;
+ padding-top: 16px;
+ padding-bottom: 20px;
.menu-icon {
padding-right: 13px;
@@ -230,7 +165,6 @@
.item {
display: inline-block;
width: 100%;
- font-family: Verdana, sans-serif;
.font-default(13px, 21px);
padding: 12px 21px 12px 19px;
color: @theme-color-menu-contrast-text;
@@ -356,8 +290,33 @@
#root {
.top_controls {
+
visibility: hidden;
opacity: 0;
+ z-index: 45;
+
+ .quick-access {
+ z-index: 1000;
+ position: absolute;
+ width: 208px;
+ margin-left: 16px;
+
+ .dropdown {
+ width: 209px;
+ padding: 0;
+ }
+
+ input {
+ height: 33px;
+ font-size: 11px;
+ padding: 10px 12px 10px 10px;
+ border: 0;
+ margin: 0;
+ box-sizing: border-box;
+ border-radius: 2px !important
+ }
+ }
+
.piwikTopControl {
margin-top: 8px;
margin-bottom: 8px;
@@ -371,13 +330,14 @@
position: relative;
background-color: @theme-color-background-base;
height: auto;
- margin-bottom: 10px;
+ margin-bottom: 8px;
+ margin-top: 8px;
.piwikTopControl {
display: inline-block;
float: none;
- position: absolute;
- margin-right: 18px;
+ position: relative;
+ margin-right: 16px;
vertical-align: top;
font-size: 11px;
}
@@ -387,11 +347,13 @@
#root, #standalone, .ui-dialog, .ngdialog {
.borderedControl {
background-color: @theme-color-background-base;
- border: 1px solid @theme-color-background-tinyContrast;
+ border: 0;
transition: box-shadow 150ms linear;
+ box-shadow: 0 1px 4px rgba(0,0,0,0.3);
+ border-radius: 2px;
&.expanded,
&:hover, &:focus {
- box-shadow: 0 1px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
+ background-color: @theme-color-background-contrast;
}
}
}
diff --git a/plugins/CoreHome/stylesheets/notification.less b/plugins/CoreHome/stylesheets/notification.less
index cab47a40a5..6d36553cb1 100644
--- a/plugins/CoreHome/stylesheets/notification.less
+++ b/plugins/CoreHome/stylesheets/notification.less
@@ -1,4 +1,4 @@
-#content.admin #notificationContainer {
+#notificationContainer {
.notification {
margin: 0 0 10px 0;
}
diff --git a/plugins/CoreHome/stylesheets/promo.less b/plugins/CoreHome/stylesheets/promo.less
index 6b3641d185..ab0ed8f124 100644
--- a/plugins/CoreHome/stylesheets/promo.less
+++ b/plugins/CoreHome/stylesheets/promo.less
@@ -1,7 +1,8 @@
#piwik-promo-thumbnail {
background: @theme-color-background-base url(plugins/CoreHome/images/promo_splash.png) no-repeat 0 0;
background-position: center;
- width: 321px;
+ width: 100%;
+ max-width: 321px;
margin: 0 auto 0 auto;
}
diff --git a/plugins/CoreHome/stylesheets/zen-mode.less b/plugins/CoreHome/stylesheets/zen-mode.less
index 520d501d32..d15814952a 100644
--- a/plugins/CoreHome/stylesheets/zen-mode.less
+++ b/plugins/CoreHome/stylesheets/zen-mode.less
@@ -1,15 +1,25 @@
-#content:not(.admin), .widget, .ui-widget {
+.zenMode {
+ nav {
+ display: none;
+ }
- .UserCountryMap .dataTableFooterIcons {
- display: block;
+ #secondNavBar {
+ display: none;
}
+ #root #secondNavBar + .pageWrap {
+ margin-left: 0;
+ }
+}
+
+#content:not(.admin), .widget, .ui-widget {
+
/* DATATABLES */
table.dataTable {
th.sortable {
- border-left: 0px;
- border-bottom: 0px;
+ border-left: 0;
+ border-bottom: 0;
}
th.columnSorted {
font-weight: normal !important;
@@ -21,8 +31,8 @@
font-weight: bold !important;
}
tr td {
- padding-top: 7px;
- padding-bottom: 7px;
+ padding-top: 10px;
+ padding-bottom: 10px;
}
}
@@ -38,15 +48,15 @@
}
.dataTableVizHtmlTable > .dataTableWrapper {
- width: 500px;
+ width: 100%;
}
.dataTableFooterNavigation {
- padding: 7px 0;
- }
+ padding: 7px 0 0 0;
- .dataTableFooterIcons {
- border-top: 0px;
+ > .row {
+ margin-bottom: 0;
+ }
}
.goalEntry:first-of-type {
@@ -61,58 +71,8 @@
display: none;
}
- div[data-report="Goals.getItemsSku"] > .dataTableWrapper,
- div[data-report="Goals.getItemsName"] > .dataTableWrapper,
- div[data-report="Goals.getItemsCategory"] > .dataTableWrapper,
- div[data-report="Referrers.getReferrerType"] > .dataTableWrapper,
- div[data-report="Referrers.getAll"] > .dataTableWrapper {
- width: 800px;
- }
-
.reportsByDimensionView .entityList {
margin-left: 13px;
}
}
-
-#content:not(.admin), .widget {
-
- .reporting-page {
- .smallTopMargin:not(.graphEvolution) {
- margin-top: 20px;
- }
- }
-
- h2 {
- margin-top: 40px;
- padding: 16px 0 12px 0;
- font-size: 21px;
-
- &.noTopMargin {
- margin-top: 0;
- padding-top: 7px;
- }
- }
-
- .widget [piwik-widget-container] [piwik-widget]:first-child h2 {
- // eg Visits Overview with Graph should not have a margin-top
- margin-top: 0px;
- }
-}
-
-#root #dashboard .widget {
- h2 {
- padding-left: 10px;
- }
-}
-
-.widget {
- h2:nth-of-type(n+2) {
- padding-top: 40px!important;
- margin-top: 0!important;
- }
-}
-
-.dataTableFeatures .expandDataTableFooterDrawer {
- margin-bottom: -9px;
-}
diff --git a/plugins/CoreHome/templates/_dataTable.twig b/plugins/CoreHome/templates/_dataTable.twig
index 697af2c9b6..21ecf30558 100644
--- a/plugins/CoreHome/templates/_dataTable.twig
+++ b/plugins/CoreHome/templates/_dataTable.twig
@@ -2,6 +2,30 @@
{% include visualizationTemplate %}
{%- else -%}
+{% set isDataTableEmpty = (dataTable is empty or dataTableHasNoData|default(false)) %}
+
+{% set showCardAsContentBlock = (properties.show_as_content_block and properties.show_title and not isWidget) %}
+{% set showOnlyTitleWithoutCard = not showCardAsContentBlock and properties.title and properties.show_title %}
+
+{% if showCardAsContentBlock %}
+<div class="card">
+<div class="card-content">
+ {% if properties.title %}
+ <h2 class="card-title"
+ piwik-enriched-headline
+ >{{ properties.title }}</h2>
+ {% endif %}
+{% elseif showOnlyTitleWithoutCard %}
+ <div>
+ <h2>{{ properties.title }}</h2>
+{% endif %}
+
+{% set showCardTableIsEmpty = not properties.show_as_content_block and isDataTableEmpty and not isWidget %}
+{% if showCardTableIsEmpty %}
+ <div class="card">
+ <div class="card-content">
+{% endif %}
+
{% set summaryRowId = constant('Piwik\\DataTable::ID_SUMMARY_ROW') %}{# ID_SUMMARY_ROW #}
{% set isSubtable = javascriptVariablesToSet.idSubtable is defined and javascriptVariablesToSet.idSubtable != 0 %}
<div class="dataTable {{ visualizationCssClass }} {{ properties.datatable_css_class|default('') }} {% if isSubtable %}subDataTable{% endif %}"
@@ -10,15 +34,17 @@
data-report-metadata="{{ reportMetdadata|json_encode|e('html_attr') }}"
data-props="{% if clientSideProperties is empty %}{}{% else %}{{ clientSideProperties|json_encode }}{% endif %}"
data-params="{% if clientSideParameters is empty %}{}{% else %}{{ clientSideParameters|json_encode }}{% endif %}">
+
<div class="reportDocumentation">
{% if properties.documentation|default is not empty %}<p>{{ properties.documentation|raw }}</p>{% endif %}
- {% if reportLastUpdatedMessage is defined %}<span class='helpDate'>{{ reportLastUpdatedMessage|raw }}</span>{% endif %}
+ {% if reportLastUpdatedMessage is defined and reportLastUpdatedMessage %}<span class='helpDate'>{{ reportLastUpdatedMessage|raw }}</span>{% endif %}
</div>
+
<div class="dataTableWrapper">
{% if error is defined %}
{{ error.message }}
{% else %}
- {% if dataTable is empty or dataTableHasNoData|default(false) %}
+ {% if isDataTableEmpty %}
<div class="pk-emptyDataTable">
{% if showReportDataWasPurgedMessage is defined and showReportDataWasPurgedMessage %}
{{ 'CoreHome_DataForThisReportHasBeenPurged'|translate(deleteReportsOlderThan) }}
@@ -33,9 +59,20 @@
{% if properties.show_footer %}
{% include "@CoreHome/_dataTableFooter.twig" %}
{% endif %}
+
{% include "@CoreHome/_dataTableJS.twig" %}
{% endif %}
</div>
</div>
+{% if showCardTableIsEmpty %}
+ </div></div>
+{% endif %}
+
+{% if showCardAsContentBlock %}
+ </div></div>
+{% elseif showOnlyTitleWithoutCard %}
+ </div>
+{% endif %}
+
{%- endif %} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/_dataTableActions.twig b/plugins/CoreHome/templates/_dataTableActions.twig
new file mode 100644
index 0000000000..2f4c813b4e
--- /dev/null
+++ b/plugins/CoreHome/templates/_dataTableActions.twig
@@ -0,0 +1,114 @@
+ {% set randomIdForDropdown = random(999999) %}
+
+ {% if properties.show_footer and properties.show_footer_icons %}
+
+ <a class='dropdown-button dropdownConfigureIcon dataTableAction'
+ href='javascript:;'
+ data-activates='dropdownConfigure{{ randomIdForDropdown }}'><span class="icon-configure"></span></a>
+
+ {% set activeFooterIcon = '' %}
+ {% set numIcons = 0 %}
+ {% set visualizationIcons %}
+ <ul id='dropdownVisualizations{{ randomIdForDropdown }}' class='dropdown-content dataTableFooterIcons'>
+ {% for footerIconGroup in footerIcons %}
+ {% for footerIcon in footerIconGroup.buttons if footerIcon.icon %}
+ <li>
+ {% set numIcons = numIcons + 1 %}
+ {% set isActiveEcommerceView = clientSideParameters.abandonedCarts is defined and
+ ((footerIcon.id == 'ecommerceOrder' and clientSideParameters.abandonedCarts == 0) or
+ (footerIcon.id == 'ecommerceAbandonedCart' and clientSideParameters.abandonedCarts == 1)) %}
+ <a class="{{ footerIconGroup.class }} tableIcon {% if clientSideParameters.viewDataTable == footerIcon.id or isActiveEcommerceView %}activeIcon{% set activeFooterIcon = footerIcon.icon %}{% endif %}"
+ data-footer-icon-id="{{ footerIcon.id }}">
+ {% if footerIcon.icon starts with 'icon-' %}
+ <span title="{{ footerIcon.title }}" class="{{ footerIcon.icon }}"></span>
+ {% else %}
+ <img width="16" height="16" title="{{ footerIcon.title }}" src="{{ footerIcon.icon }}"/>
+ {% endif %}
+ {% if footerIcon.title is defined %}<span>{{ footerIcon.title }}</span>{% endif %}
+ </a>
+ </li>
+ {% endfor %}
+ <li class="divider"></li>
+ {% endfor %}
+ </ul>
+ {% endset %}
+
+ {% if activeFooterIcon and numIcons > 1 %}
+ <a class="dropdown-button dataTableAction activateVisualizationSelection"
+ href="javascript:;"
+ data-activates="dropdownVisualizations{{ randomIdForDropdown }}">
+ {% if activeFooterIcon starts with 'icon-' %}
+ <span title="{{ 'CoreHome_ChangeVisualization'|translate|e('html_attr') }}" class="{{ activeFooterIcon }}"></span>
+ {% else %}
+ <img title="{{ 'CoreHome_ChangeVisualization'|translate|e('html_attr') }}" width="16" height="16" src="{{ activeFooterIcon }}"/>
+ {% endif %}
+ </a>
+ {{ visualizationIcons|raw }}
+ {% endif %}
+
+ <a class='dropdown-button dataTableAction activateExportSelection'
+ href='javascript:;' title="{{ 'General_ExportThisReport'|translate|e('html_attr') }}"
+ data-activates='dropdownExport{{ randomIdForDropdown }}'><span class="icon-export"></span></a>
+
+ {% if isPluginLoaded('Annotations') and not properties.hide_annotations_view %}
+ <a class='dataTableAction annotationView'
+ href='javascript:;' title="{{ 'Annotations_Annotations'|translate|e('html_attr') }}"
+ ><span class="icon-annotation"></span></a>
+ {% endif %}
+
+ {% if properties.show_search %}
+ <a class='dropdown-button dataTableAction searchAction'
+ href='javascript:;' title="{{ 'General_Search'|translate|e('html_attr') }}"
+ ><span class="icon-search"></span>
+ <span class="icon-close" title="{{ 'CoreHome_CloseSearch'|translate|e('html_attr') }}"></span>
+ <input id="widgetSearch_{{ properties.report_id }}"
+ title="{{ 'CoreHome_DataTableHowToSearch'|translate|e('html_attr') }}"
+ type="text"
+ class="dataTableSearchInput browser-default" />
+ </a>
+ {% endif %}
+
+ <ul id='dropdownExport{{ randomIdForDropdown }}' class='dropdown-content exportToFormatItems'>
+ {% set requestParams = properties.request_parameters_to_modify|json_encode %}
+ <li><a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="CSV" filter_limit="{{ properties.export_limit }}">CSV</a></li>
+ <li><a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="TSV" filter_limit="{{ properties.export_limit }}">TSV (Excel)</a></li>
+ <li><a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="XML" filter_limit="{{ properties.export_limit }}">XML</a></li>
+ <li><a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="JSON" filter_limit="{{ properties.export_limit }}">Json</a></li>
+ <li><a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="PHP" filter_limit="{{ properties.export_limit }}">Php</a></li>
+ {% if properties.show_export_as_rss_feed %}
+ <li><a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="RSS" filter_limit="{{ properties.export_limit }}" date="last10">
+ <span class="icon-feed"></span> RSS
+ </a>
+ </li>
+ {% endif %}
+ {% if properties.show_export_as_image_icon %}
+ <li><a class="tableIcon" href="javascript:;" id="dataTableFooterExportAsImageIcon" onclick="$(this).closest('.dataTable').find('div.jqplot-target').trigger('piwikExportAsImage'); return false;">
+ <span class="icon-image"></span>
+ {{ 'General_ExportAsImage'|translate }}
+ </a>
+ </li>
+ {% endif %}
+ </ul>
+ <ul id='dropdownConfigure{{ randomIdForDropdown }}' class='dropdown-content tableConfiguration'>
+ {% if properties.show_flatten_table %}
+ {% if clientSideParameters.flat is defined and clientSideParameters.flat == 1 %}
+ <li>
+ <div class="configItem dataTableIncludeAggregateRows"></div>
+ </li>
+ {% endif %}
+ <li>
+ <div class="configItem dataTableFlatten"></div>
+ </li>
+ {% endif %}
+ {% if properties.show_exclude_low_population %}
+ <li>
+ <div class="configItem dataTableExcludeLowPopulation"></div>
+ </li>
+ {% endif %}
+ {% if properties.show_pivot_by_subtable|default is not empty %}
+ <li>
+ <div class="configItem dataTablePivotBySubtable"></div>
+ </li>
+ {% endif %}
+ </ul>
+ {% endif %} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/_dataTableFooter.twig b/plugins/CoreHome/templates/_dataTableFooter.twig
index 7401baee10..ef043e6090 100644
--- a/plugins/CoreHome/templates/_dataTableFooter.twig
+++ b/plugins/CoreHome/templates/_dataTableFooter.twig
@@ -1,144 +1,49 @@
<div class="dataTableFeatures">
-
<div class="dataTableFooterNavigation">
- {% if properties.show_offset_information %}
- <span>
- <span class="dataTablePages"></span>
- </span>
- {% endif %}
-
- {% if properties.show_pagination_control %}
- <span>
- <span class="dataTablePrevious">&lsaquo; {% if clientSideParameters.dataTablePreviousIsFirst is defined %}{{ 'General_First'|translate }}{% else %}{{ 'General_Previous'|translate }}{% endif %} </span>
- <span class="dataTableNext">{{ 'General_Next'|translate }} &rsaquo;</span>
- </span>
- {% endif %}
- {% if properties.show_search %}
- <span class="dataTableSearchPattern">
- <label for="widgetSearch_{{ properties.report_id }}" style="display:none;"> {{ 'General_Search'|translate }} {{ visualization.config.translations.label|default('') }}</label>
- <input id="widgetSearch_{{ properties.report_id }}" type="text" class="searchInput" length="15" />
- <input type="submit" value="{{ 'General_Search'|translate }}" />
- </span>
+ {% if not isDataTableEmpty and (properties.show_offset_information or properties.show_pagination_control) %}
+ <div class="row dataTablePaginationControl">
+ {% if properties.show_pagination_control %}
+ <span class="dataTablePrevious">&lsaquo; {% if clientSideParameters.dataTablePreviousIsFirst is defined %}{{ 'General_First'|translate }}{% else %}{{ 'General_Previous'|translate }}{% endif %}</span>
+ &nbsp;
+ {% endif %}
+ {% if properties.show_offset_information %}
+ <span class="dataTablePages"></span>
+ {% endif %}
+ {% if properties.show_pagination_control %}
+ &nbsp;<span class="dataTableNext">{{ 'General_Next'|translate }} &rsaquo;</span>
+ {% endif %}
+ </div>
{% endif %}
- </div>
-
- <span class="loadingPiwik" style="display:none;"><img src="plugins/Morpheus/images/loading-blue.gif"/> {{ 'General_LoadingData'|translate }}</span>
-
- {% if properties.show_footer_icons %}
- <div class="dataTableFooterIcons">
- <div class="dataTableFooterWrap">
- {% for footerIconGroup in footerIcons %}
- <div class="tableIconsGroup">
- <span class="{{ footerIconGroup.class }}">
- {% for footerIcon in footerIconGroup.buttons if footerIcon.icon %}
- {% set isActiveEcommerceView = clientSideParameters.abandonedCarts is defined and
- ((footerIcon.id == 'ecommerceOrder' and clientSideParameters.abandonedCarts == 0) or
- (footerIcon.id == 'ecommerceAbandonedCart' and clientSideParameters.abandonedCarts == 1)) %}
- <span>
- {% if properties.show_active_view_icon and (clientSideParameters.viewDataTable == footerIcon.id or isActiveEcommerceView) %}
- <img src="plugins/Morpheus/images/data_table_footer_active_item.png" class="dataTableFooterActiveItem"/>
- {% endif %}
- <a class="tableIcon {% if clientSideParameters.viewDataTable == footerIcon.id or isActiveEcommerceView %}activeIcon{% endif %}" data-footer-icon-id="{{ footerIcon.id }}">
- <img width="16" height="16" title="{{ footerIcon.title }}" src="{{ footerIcon.icon }}"/>
- {% if footerIcon.text is defined %}<span>{{ footerIcon.text }}</span>{% endif %}
- </a>
- </span>
- {% endfor %}
- </span>
- </div>
- {% endfor %}
- <div class="tableIconsGroup">
- {% if footerIcons is empty %}
- <img src="plugins/Morpheus/images/data_table_footer_active_item.png" class="dataTableFooterActiveItem"/>
- {% endif %}
- <span class="exportToFormatIcons">
- <a class="tableIcon" var="export">
- <img width="16" height="16" src="plugins/Morpheus/images/export.png" title="{{ 'General_ExportThisReport'|translate }}"/>
- </a>
- </span>
- <span class="exportToFormatItems" style="display:none;">
- {{ 'General_Export'|translate }}:
- {% set requestParams = properties.request_parameters_to_modify|json_encode %}
- <a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="CSV" filter_limit="{{ properties.export_limit }}">CSV</a> |
- <a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="TSV" filter_limit="{{ properties.export_limit }}">TSV (Excel)</a> |
- <a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="XML" filter_limit="{{ properties.export_limit }}">XML</a> |
- <a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="JSON" filter_limit="{{ properties.export_limit }}">Json</a> |
- <a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="PHP" filter_limit="{{ properties.export_limit }}">Php</a>
- {% if properties.show_export_as_rss_feed %}
- |
- <a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="RSS" filter_limit="{{ properties.export_limit }}" date="last10">
- <img border="0" src="plugins/Morpheus/images/feed.png"/>
- </a>
- {% endif %}
- </span>
- {% if properties.show_export_as_image_icon %}
- <span id="dataTableFooterExportAsImageIcon">
- <a class="tableIcon" href="#" onclick="$(this).closest('.dataTable').find('div.jqplot-target').trigger('piwikExportAsImage'); return false;">
- <img title="{{ 'General_ExportAsImage'|translate }}" src="plugins/Morpheus/images/image.png"/>
- </a>
- </span>
- {% endif %}
- </div>
+ <div class="row">
+ <div class="col s9 m9 dataTableControls">
+ {% include "@CoreHome/_dataTableActions.twig" %}
</div>
- <div class="limitSelection {% if not properties.show_pagination_control and not properties.show_limit_control %} hidden{% endif %}"
- title="{{ 'General_RowsToDisplay'|translate }}" alt="{{ 'General_RowsToDisplay'|translate }}"></div>
- <div class="tableConfiguration">
- <a class="tableConfigurationIcon" href="#"></a>
- <ul>
- {% if properties.show_flatten_table %}
- {% if clientSideParameters.flat is defined and clientSideParameters.flat == 1 %}
- <li>
- <div class="configItem dataTableIncludeAggregateRows"></div>
- </li>
- {% endif %}
- <li>
- <div class="configItem dataTableFlatten"></div>
- </li>
- {% endif %}
- {% if properties.show_exclude_low_population %}
- <li>
- <div class="configItem dataTableExcludeLowPopulation"></div>
- </li>
- {% endif %}
- {% if properties.show_pivot_by_subtable|default is not empty %}
- <li>
- <div class="configItem dataTablePivotBySubtable"></div>
- </li>
- {% endif %}
- </ul>
- </div>
- {% if isPluginLoaded('Annotations') and not properties.hide_annotations_view %}
- <div class="annotationView" title="{{ 'Annotations_IconDesc'|translate }}">
- <a class="tableIcon">
- <img width="16" height="16" src="plugins/Morpheus/images/annotations.png"/>
- </a>
- <span>{{ 'Annotations_Annotations'|translate }}</span>
- </div>
- {% endif %}
-
- <div class="foldDataTableFooterDrawer" title="{{ 'General_Close'|translate|e('html_attr') }}"
- ><img width="7" height="4" src="plugins/Morpheus/images/sortasc_dark.png"></div>
+ {% if properties.show_footer_icons and properties.show_pagination_control or properties.show_limit_control %}
+ <div class="col s3 m3 limitSelection"
+ title="{{ 'General_RowsToDisplay'|translate }}" alt="{{ 'General_RowsToDisplay'|translate }}"
+ ></div>
+ {% endif %}
</div>
- <div class="expandDataTableFooterDrawer" title="{{ 'General_ExpandDataTableFooter'|translate|e('html_attr') }}"
- ><img width="7" height="4" src="plugins/Morpheus/images/sortdesc_dark.png" style=""></div>
- {% endif %}
- <div class="datatableRelatedReports">
{% if (properties.related_reports is not empty) and properties.show_related_reports %}
- {{ properties.related_reports_title|raw }}
- <ul style="list-style:none;{% if properties.related_reports|length == 1 %}display:inline-block;{% endif %}}">
- <li><span href="{{ properties.self_url }}" style="display:none;">{{ properties.title }}</span></li>
+ <div class="row datatableRelatedReports">
+ {{ properties.related_reports_title|raw }}
+ <ul style="list-style:none;{% if properties.related_reports|length == 1 %}display:inline-block;{% endif %}}">
+ <li><span href="{{ properties.self_url }}" style="display:none;">{{ properties.title }}</span></li>
- {% for reportUrl,reportTitle in properties.related_reports %}
- <li><span href="{{ reportUrl }}">{{ reportTitle }}</span></li>
- {% endfor %}
- </ul>
+ {% for reportUrl,reportTitle in properties.related_reports %}
+ <li><span href="{{ reportUrl }}">{{ reportTitle }}</span></li>
+ {% endfor %}
+ </ul>
+ </div>
{% endif %}
</div>
+ <span class="loadingPiwik" style="display:none;"><img src="plugins/Morpheus/images/loading-blue.gif"/> {{ 'General_LoadingData'|translate }}</span>
+
{% if properties.show_footer_message is defined and properties.show_footer_message is not empty %}
<div class='datatableFooterMessage'>{{ properties.show_footer_message | raw }}</div>
{% endif %}
diff --git a/plugins/CoreHome/templates/_donate.twig b/plugins/CoreHome/templates/_donate.twig
index cfee8557b8..b0ff2ad99d 100755
--- a/plugins/CoreHome/templates/_donate.twig
+++ b/plugins/CoreHome/templates/_donate.twig
@@ -4,8 +4,8 @@
{{ msg }}
{% else %}
<p>{{ 'CoreHome_DonateCall1'|translate }}</p>
- <p><strong><em>{{ 'CoreHome_DonateCall2'|translate }}</em></strong></p>
- <p>{{ 'CoreHome_DonateCall3'|translate('<em><strong>','</strong></em>')|raw }}</p>
+ <p><strong>{{ 'CoreHome_DonateCall2'|translate }}</strong></p>
+ <p>{{ 'CoreHome_DonateCall3'|translate('<strong>','</strong>')|raw }}</p>
{% endif %}
</div>
diff --git a/plugins/CoreHome/templates/_headerMessage.twig b/plugins/CoreHome/templates/_headerMessage.twig
index 5fbd78125d..051e10f74d 100644
--- a/plugins/CoreHome/templates/_headerMessage.twig
+++ b/plugins/CoreHome/templates/_headerMessage.twig
@@ -27,7 +27,7 @@
{% endif %}
</a>
- <div class="dropdown">
+ <div class="dropdown positionInViewport">
{% if isPiwikDemo %}
{{ 'General_DownloadFullVersion'|translate("<a href='http://piwik.org/'>","</a>","<a href='http://piwik.org'>piwik.org</a>")|raw }}
<br/>
diff --git a/plugins/CoreHome/templates/_logo.twig b/plugins/CoreHome/templates/_logo.twig
index 633985a20d..6d9eecc1b3 100644
--- a/plugins/CoreHome/templates/_logo.twig
+++ b/plugins/CoreHome/templates/_logo.twig
@@ -1,10 +1,13 @@
-<span id="logo">
- <a href="index.php" tabindex="-1" title="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik # {{ 'General_OpenSourceWebAnalytics'|translate }}">
+<span id="logo" class="brand-logo">
+ <a href="index.php" tabindex="-1"
+ title="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik # {{ 'General_OpenSourceWebAnalytics'|translate }}"
+ >
{% if hasSVGLogo %}
- <img src='{{ logoSVG }}' tabindex="3" alt="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik" class="ie-hide {% if not isCustomLogo %}default-piwik-logo{% endif %}" />
- <!--[if lt IE 9]>
- {% endif %}
+ <img src='{{ logoSVG }}' tabindex="3"
+ alt="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik"
+ class="{% if not isCustomLogo %}default-piwik-logo{% endif %}" />
+ {% else %}
<img src='{{ logoHeader }}' alt="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik" />
- {% if hasSVGLogo %}<![endif]-->{% endif %}
+ {% endif %}
</a>
</span>
diff --git a/plugins/CoreHome/templates/_menu.twig b/plugins/CoreHome/templates/_menu.twig
index 673c2ba1c8..7fa08961ec 100644
--- a/plugins/CoreHome/templates/_menu.twig
+++ b/plugins/CoreHome/templates/_menu.twig
@@ -1,9 +1,6 @@
{% macro menu(menu, anchorlink, cssClass, currentModule, currentAction) %}
- <div id="secondNavBar" class="{{ cssClass }}">
- <div id="search" ng-cloak>
- <div piwik-quick-access class="borderedControl"></div>
- </div>
- <ul class="navbar" aria-label="{{ 'CoreHome_MainNavigation'|translate|e('html_attr') }}" role="menu">
+ <div id="secondNavBar" class="{{ cssClass }} z-depth-1">
+ <ul class="navbar hide-on-med-and-down" aria-label="{{ 'CoreHome_MainNavigation'|translate|e('html_attr') }}" role="menu">
{% for level1,level2 in menu %}
{% set hasSubmenuItem = false %}
@@ -41,5 +38,38 @@
{% endif %}
{% endfor %}
</ul>
+ <ul id="mobile-left-menu" class="side-nav hide-on-large-only">
+ {% for level1,level2 in menu %}
+
+ {% set hasSubmenuItem = false %}
+ {% for name,urlParameters in level2 %}
+ {% if name|slice(0,1) != '_' %}
+ {% set hasSubmenuItem = true %}
+ {% endif %}
+ {% endfor %}
+
+ {% if hasSubmenuItem %}
+ <li class="no-padding">
+ <ul class="collapsible collapsible-accordion" piwik-side-nav="nav .activateLeftMenu">
+ <li>
+ <a class="collapsible-header">{{ level1|translate }}<i class="{{ level2._icon|default('icon-arrow-down') }}"></i></a>
+ <div class="collapsible-body">
+ <ul>
+ {% for name,urlParameters in level2 %}
+ {% if name|slice(0,1) != '_' %}
+ <li>
+ <a title="{{ urlParameters._tooltip|default('')|translate|e('html_attr') }}"
+ href="index.php?{{ urlParameters._url|urlRewriteWithParameters|slice(1) }}">{{ name|translate }}</a>
+ </li>
+ {% endif %}
+ {% endfor %}
+ </ul>
+ </div>
+ </li>
+ </ul>
+ {% endif %}
+ {% endfor %}
+ </li>
+ </ul>
</div>
{% endmacro %}
diff --git a/plugins/CoreHome/templates/_periodSelect.twig b/plugins/CoreHome/templates/_periodSelect.twig
index 4528109f5f..c02f14d1a6 100644
--- a/plugins/CoreHome/templates/_periodSelect.twig
+++ b/plugins/CoreHome/templates/_periodSelect.twig
@@ -4,34 +4,43 @@
{{ prettyDate }}
</a>
<div id="periodMore" class="dropdown">
- <div class="period-date">
- <div id="datepicker"></div>
- </div>
- <div class="period-range" style="display:none;">
- <div id="calendarRangeFrom">
- <h6>{{ 'General_DateRangeFrom'|translate }}<input tabindex="4" type="text" id="inputCalendarFrom" name="inputCalendarFrom"/></h6>
+ <table>
+ <tr>
+ <td>
+ <div class="period-range" style="display:none;">
+ <div id="calendarRangeFrom">
+ <h6>{{ 'General_DateRangeFrom'|translate }}<input tabindex="4" type="text" id="inputCalendarFrom" name="inputCalendarFrom" class="browser-default"/></h6>
- <div id="calendarFrom"></div>
- </div>
- <div id="calendarRangeTo">
- <h6>{{ 'General_DateRangeTo'|translate }}<input tabindex="4" type="text" id="inputCalendarTo" name="inputCalendarTo"/></h6>
+ <div id="calendarFrom"></div>
+ </div>
+ <div id="calendarRangeTo">
+ <h6>{{ 'General_DateRangeTo'|translate }}<input tabindex="4" type="text" id="inputCalendarTo" name="inputCalendarTo" class="browser-default"/></h6>
- <div id="calendarTo"></div>
- </div>
- </div>
- <div class="period-type">
- <h6>{{ 'General_Period'|translate }}</h6>
- <span id="otherPeriods">
- {% for label,thisPeriod in periodsNames %}
- <input type="radio" name="period" id="period_id_{{ label }}" value="{{ linkTo( { 'period': label} ) }}"{% if label==period %} checked="checked"{% endif %} />
- <label for="period_id_{{ label }}">{{ thisPeriod.singular }}</label>
- <br/>
- {% endfor %}
- </span>
- <input tabindex="4" type="submit" value="{{ 'General_Apply'|translate }}" id="calendarApply" class="btn"/>
- {% import 'ajaxMacros.twig' as ajax %}
- {{ ajax.loadingDiv('ajaxLoadingCalendar') }}
- </div>
+ <div id="calendarTo"></div>
+ </div>
+ </div>
+ <div class="period-date">
+ <div id="datepicker"></div>
+ </div>
+ </td>
+ <td>
+ <div class="period-type">
+ <h6>{{ 'General_Period'|translate }}</h6>
+ <span id="otherPeriods">
+ {% for label,thisPeriod in periodsNames %}
+ <p>
+ <input type="radio" name="period" id="period_id_{{ label }}" value="{{ linkTo( { 'period': label} ) }}"{% if label==period %} checked="checked"{% endif %} />
+ <label for="period_id_{{ label }}">{{ thisPeriod.singular }}</label>
+ </p>
+ {% endfor %}
+ </span>
+ <input tabindex="4" type="submit" value="{{ 'General_Apply'|translate }}" id="calendarApply" class="btn"/>
+ {% import 'ajaxMacros.twig' as ajax %}
+ {{ ajax.loadingDiv('ajaxLoadingCalendar') }}
+ </div>
+ </td>
+ </tr>
+ </table>
</div>
<div class="period-click-tooltip" style="display:none;">{{ 'General_ClickToChangePeriod'|translate }}</div>
</div>
diff --git a/plugins/CoreHome/templates/_topBar.twig b/plugins/CoreHome/templates/_topBar.twig
index 1d45c7282e..74500aadcf 100644
--- a/plugins/CoreHome/templates/_topBar.twig
+++ b/plugins/CoreHome/templates/_topBar.twig
@@ -1,25 +1,25 @@
{{ postEvent("Template.beforeTopBar", userAlias, userLogin, topMenu) }}
-<ul role="menubar" class="navbar-right">
-
+<ul class="right hide-on-med-and-down">
{% macro menuItemLabel(label, icon) %}
{% if icon is defined and icon and icon starts with 'icon-' %}
- <span class="{{ icon|striptags }}"></span>
+ <span class="navbar-icon {{ icon|striptags }}" aria-label="{{ label|translate|e('html_attr') }}"></span>
{% else %}
{{ label|translate }}
{% endif %}
{% endmacro %}
- {% macro topMenuItem(label, menu, currentModule, currentAction) %}
+ {% macro topMenuItem(label, icon, menu) %}
{% if menu._html is defined %}
{{ menu._html|raw }}
{% else %}
<a {% if menu._tooltip is defined %}title="{{ menu._tooltip }}"{% endif %}
- class="topBarElem {% if (menu._url.module == currentModule and (menu._url.action is empty or menu._url.action == currentAction)) %}active{% endif %}"
id="topmenu-{{ menu._url.module|lower }}"
- href="index.php{{ menu._url|urlRewriteWithParameters }}" tabindex="3">{{ _self.menuItemLabel(label, menu._icon) }}</a>
+ href="index.php{{ menu._url|urlRewriteWithParameters }}" tabindex="3">{{ _self.menuItemLabel(label, icon) }}</a>
{% endif %}
{% endmacro %}
+ {% macro isActiveItem(menu, currentModule, currentAction) %}{% if (menu and menu._url and menu._url.module == currentModule and (menu._url.action is empty or menu._url.action == currentAction)) %}active{% endif %}{% endmacro %}
+
{% if topMenuModule is not defined %}
{% set topMenuModule = currentModule %}
{% set topMenuAction = currentAction %}
@@ -27,8 +27,15 @@
{% spaceless %}
{% for label,menu in topMenu %}
- <li role="menuitem">{{ _self.topMenuItem(label, menu, topMenuModule, topMenuAction) }}</li>
+ <li role="menuitem" class="{{ _self.isActiveItem(menu, topMenuModule, topMenuAction) }}">{{ _self.topMenuItem(label, menu._icon, menu) }}</li>
{% endfor %}
{% endspaceless %}
-
-</ul> \ No newline at end of file
+</ul>
+<ul class="side-nav" id="mobile-top-menu">
+ {% for label,menu in topMenu %}
+ <li role="menuitem" class="{{ _self.isActiveItem(menu, topMenuModule, topMenuAction) }}"
+ >{{ _self.topMenuItem(label, '', menu) }}</li>
+ {% endfor %}
+</ul>
+<a href="javascript:;" data-activates="mobile-left-menu" class="activateLeftMenu hide-on-large-only button-collapse" style="display:none;"><span class="icon-menu-hamburger"></span></a>
+<a href="javascript:;" data-activates="mobile-top-menu" class="activateTopMenu hide-on-large-only button-collapse"><span class="icon-more-verti"></span></a> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/_topScreen.twig b/plugins/CoreHome/templates/_topScreen.twig
index f0ba5fc147..f85fb434d8 100644
--- a/plugins/CoreHome/templates/_topScreen.twig
+++ b/plugins/CoreHome/templates/_topScreen.twig
@@ -1,15 +1,7 @@
-<div id="header" class="container-fluid">
- <a href="#main" tabindex="1" class="accessibility-skip-to-content">{{'CoreHome_SkipToContent'|translate}}</a>
- <div id="topRightBar" class="navbar row">
- <div class="navbar-header col-md-3">
- <span class="toggle-second-menu icon-menu-hamburger"></span>
- {% include "@CoreHome/_logo.twig" %}
-
- <!-- we need to put button to toggle nav for responsiveness here -->
-
- </div>
- <div class="collapse navbar-collapse col-md-9" id="navbar-collapse1">
- {% include "@CoreHome/_topBar.twig" %}
- </div>
+<nav>
+ <div class="nav-wrapper">
+ <a href="#main" tabindex="1" class="accessibility-skip-to-content">{{'CoreHome_SkipToContent'|translate}}</a>
+ {% include "@CoreHome/_logo.twig" %}
+ {% include "@CoreHome/_topBar.twig" %}
</div>
-</div> \ No newline at end of file
+</nav> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/getSystemSummary.twig b/plugins/CoreHome/templates/getSystemSummary.twig
new file mode 100644
index 0000000000..75cdf0a935
--- /dev/null
+++ b/plugins/CoreHome/templates/getSystemSummary.twig
@@ -0,0 +1,28 @@
+<div class="widgetBody systemSummary">
+ <div>
+ <span class="icon icon-user"></span>
+ <a href="{{ linkTo({'module': 'UsersManager', 'action': 'index'}) }}">{{ 'General_NUsers'|translate(numUsers) }}</a>
+ </div>
+ <div>
+ <span><span class="icon icon-segment"></span> {{ 'CoreHome_SystemSummaryNSegments'|translate(numSegments) }}</span>
+ </div>
+ <div>
+ <a href="{{ linkTo({'module': 'SitesManager', 'action': 'index'}) }}">{{ 'CoreHome_SystemSummaryNWebsites'|translate(numWebsites) }}</a>
+ </div>
+ <div>
+ <a href="{{ linkTo({'module': 'CorePluginsAdmin', 'action': 'plugins'}) }}">{{ 'CoreHome_SystemSummaryNActivatedPlugins'|translate(numPlugins) }}</a>
+ </div>
+ <div>
+ <span>{{ 'CoreHome_SystemSummaryPiwikVersion'|translate }}:</span>
+ <span>{{ piwikVersion }}</span>
+ </div>
+ <div>
+ <span>{{ 'CoreHome_SystemSummaryMysqlVersion'|translate }}:</span>
+ <span>{{ mySqlVersion }}</span>
+ </div>
+ <div>
+ <span>{{ 'CoreHome_SystemSummaryPhpVersion'|translate }}:</span>
+ <span>{{ phpVersion }}</span>
+ </div>
+ <br />
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/widgetContainer.twig b/plugins/CoreHome/templates/widgetContainer.twig
index b3eda12997..06121d1eba 100755
--- a/plugins/CoreHome/templates/widgetContainer.twig
+++ b/plugins/CoreHome/templates/widgetContainer.twig
@@ -7,11 +7,14 @@
$(function () {
var piwikWidget = $('[piwik-widget][containerid={{ containerId|e('js') }}]');
+ var isExportedAsWidget = $('body > .widget').size();
- angular.element(document).injector().invoke(function($compile) {
- var scope = angular.element(piwikWidget).scope();
- $compile(piwikWidget)(scope.$new());
- });
+ if (!isExportedAsWidget) {
+ angular.element(document).injector().invoke(function($compile) {
+ var scope = angular.element(piwikWidget).scope();
+ $compile(piwikWidget)(scope.$new());
+ });
+ }
});
</script>
diff --git a/plugins/CoreHome/tests/Integration/Tracker/VisitRequestProcessorTest.php b/plugins/CoreHome/tests/Integration/Tracker/VisitRequestProcessorTest.php
index 02ae4c6d99..965317a5e4 100644
--- a/plugins/CoreHome/tests/Integration/Tracker/VisitRequestProcessorTest.php
+++ b/plugins/CoreHome/tests/Integration/Tracker/VisitRequestProcessorTest.php
@@ -110,7 +110,9 @@ class VisitRequestProcessorTest extends IntegrationTestCase
{
$dimensions = array();
foreach ($dimensionOnNewVisitResults as $onNewVisitResult) {
- $dim = $this->getMock('Piwik\\Plugin\\Dimension', array('shouldForceNewVisit', 'getColumnName'));
+ $dim = $this->getMockBuilder('Piwik\\Plugin\\Dimension')
+ ->setMethods(array('shouldForceNewVisit', 'getColumnName'))
+ ->getMock();
$dim->expects($this->any())->method('shouldForceNewVisit')->will($this->returnValue($onNewVisitResult));
$dimensions[] = $dim;
}
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php
index 5d3b90217f..fc201f4c2d 100644
--- a/plugins/CorePluginsAdmin/Controller.php
+++ b/plugins/CorePluginsAdmin/Controller.php
@@ -51,7 +51,7 @@ class Controller extends Plugin\ControllerAdmin
parent::__construct();
}
-
+
public function marketplace()
{
self::dieIfMarketplaceIsDisabled();
@@ -72,6 +72,16 @@ class Controller extends Plugin\ControllerAdmin
$view->showThemes = $showThemes;
$view->query = $query;
$view->sort = $sort;
+ $view->pluginType = $show;
+ $view->pluginTypeOptions = array(
+ 'plugins' => Piwik::translate('General_Plugins'),
+ 'themes' => Piwik::translate('CorePluginsAdmin_Themes')
+ );
+ $view->pluginSortOptions = array(
+ 'popular' => Piwik::translate('CorePluginsAdmin_SortByPopular'),
+ 'newest' => Piwik::translate('CorePluginsAdmin_SortByNewest'),
+ 'alpha' => Piwik::translate('CorePluginsAdmin_SortByAlpha'),
+ );
$view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
$view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
$view->isSuperUser = Piwik::hasUserSuperUserAccess();
@@ -325,11 +335,10 @@ class Controller extends Plugin\ControllerAdmin
$suffix = "You may uninstall the plugin or manually delete the files in piwik/plugins/$pluginName/";
}
- $description = '<strong><em>'
+ $description = '<strong>'
. $this->translator->translate('CorePluginsAdmin_PluginNotCompatibleWith', array($pluginName, self::getPiwikVersion()))
. '</strong><br/>'
- . $suffix
- . '</em>';
+ . $suffix;
$plugin['info'] = array(
'description' => $description,
'version' => $this->translator->translate('General_Unknown'),
diff --git a/plugins/CorePluginsAdmin/CorePluginsAdmin.php b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
index dc550eead3..21217f7743 100644
--- a/plugins/CorePluginsAdmin/CorePluginsAdmin.php
+++ b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
@@ -28,6 +28,7 @@ class CorePluginsAdmin extends \Piwik\Plugin
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/marketplace.less";
+ $stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/marketplace-widget.less";
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugins_admin.less";
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugin-details.less";
$stylesheets[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less";
@@ -47,10 +48,6 @@ class CorePluginsAdmin extends \Piwik\Plugin
{
$jsFiles[] = "libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js";
$jsFiles[] = "plugins/CoreHome/javascripts/popover.js";
- $jsFiles[] = "plugins/CorePluginsAdmin/javascripts/marketplace.js";
- $jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginOverview.js";
- $jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginExtend.js";
- $jsFiles[] = "plugins/CorePluginsAdmin/javascripts/plugins.js";
}
public function getClientSideTranslationKeys(&$translations)
diff --git a/plugins/CorePluginsAdmin/Marketplace.php b/plugins/CorePluginsAdmin/Marketplace.php
index 5fcd51641d..c8cfc114e8 100644
--- a/plugins/CorePluginsAdmin/Marketplace.php
+++ b/plugins/CorePluginsAdmin/Marketplace.php
@@ -140,7 +140,9 @@ class Marketplace
{
$plugin['isInstalled'] = \Piwik\Plugin\Manager::getInstance()->isPluginLoaded($plugin['name']);
$plugin['canBeUpdated'] = $plugin['isInstalled'] && $this->hasPluginUpdate($plugin);
- $plugin['lastUpdated'] = Date::factory($plugin['lastUpdated'])->getLocalized(Date::DATE_FORMAT_SHORT);
+ if (!empty($plugin['lastUpdated'])) {
+ $plugin['lastUpdated'] = Date::factory($plugin['lastUpdated'])->getLocalized(Date::DATE_FORMAT_SHORT);
+ }
if ($plugin['canBeUpdated']) {
$pluginUpdate = $this->getPluginUpdateInformation($plugin);
@@ -159,7 +161,9 @@ class Marketplace
if (!empty($plugin['versions'])) {
foreach ($plugin['versions'] as $index => $version) {
- $plugin['versions'][$index]['release'] = Date::factory($version['release'])->getLocalized(Date::DATE_FORMAT_LONG);
+ if (!empty($version['release'])) {
+ $plugin['versions'][$index]['release'] = Date::factory($version['release'])->getLocalized(Date::DATE_FORMAT_LONG);
+ }
}
}
diff --git a/plugins/CorePluginsAdmin/MarketplaceApiClient.php b/plugins/CorePluginsAdmin/MarketplaceApiClient.php
index 0423f72600..61246f395d 100644
--- a/plugins/CorePluginsAdmin/MarketplaceApiClient.php
+++ b/plugins/CorePluginsAdmin/MarketplaceApiClient.php
@@ -125,6 +125,9 @@ class MarketplaceApiClient
private function fetch($action, $params)
{
+ $params['php'] = phpversion();
+ $params['piwik'] = Version::VERSION;
+ $params['prefer_stable'] = '1';
ksort($params);
$query = http_build_query($params);
@@ -133,7 +136,7 @@ class MarketplaceApiClient
$result = $cache->fetch($cacheId);
if (false === $result) {
- $endpoint = $this->domain . '/api/1.0/';
+ $endpoint = $this->domain . '/api/2.0/';
$url = sprintf('%s%s?%s', $endpoint, $action, $query);
$response = Http::sendHttpRequest($url, static::HTTP_REQUEST_TIMEOUT);
$result = json_decode($response, true);
@@ -161,7 +164,7 @@ class MarketplaceApiClient
private function getCacheKey($action, $query)
{
- return sprintf('marketplace.api.1.0.%s.%s', str_replace('/', '.', $action), md5($query));
+ return sprintf('marketplace.api.2.0.%s.%s', str_replace('/', '.', $action), md5($query));
}
/**
diff --git a/plugins/CorePluginsAdmin/Menu.php b/plugins/CorePluginsAdmin/Menu.php
index 7ad8af935e..69a69b9e7f 100644
--- a/plugins/CorePluginsAdmin/Menu.php
+++ b/plugins/CorePluginsAdmin/Menu.php
@@ -40,7 +40,7 @@ class Menu extends \Piwik\Plugin\Menu
}
if ($hasSuperUserAcess) {
- $menu->addManageItem(Piwik::translate('General_Plugins') . $pluginsUpdateMessage,
+ $menu->addSystemItem(Piwik::translate('General_Plugins') . $pluginsUpdateMessage,
$this->urlForAction('plugins', array('activated' => '')),
$order = 20);
}
diff --git a/plugins/CorePluginsAdmin/SettingsMetadata.php b/plugins/CorePluginsAdmin/SettingsMetadata.php
index fa62d12b11..7e9d8672ca 100644
--- a/plugins/CorePluginsAdmin/SettingsMetadata.php
+++ b/plugins/CorePluginsAdmin/SettingsMetadata.php
@@ -85,6 +85,7 @@ class SettingsMetadata
$plugin = array(
'pluginName' => $pluginName,
+ 'title' => $settings->getTitle(),
'settings' => array()
);
diff --git a/plugins/CorePluginsAdmin/Tasks.php b/plugins/CorePluginsAdmin/Tasks.php
index a168f441e1..8354694e3b 100644
--- a/plugins/CorePluginsAdmin/Tasks.php
+++ b/plugins/CorePluginsAdmin/Tasks.php
@@ -10,6 +10,16 @@ namespace Piwik\Plugins\CorePluginsAdmin;
class Tasks extends \Piwik\Plugin\Tasks
{
+ /**
+ * @var UpdateCommunication
+ */
+ private $updateCommunication;
+
+ public function __construct(UpdateCommunication $updateCommunication)
+ {
+ $this->updateCommunication = $updateCommunication;
+ }
+
public function schedule()
{
$this->daily('clearAllCacheEntries', null, self::LOWEST_PRIORITY);
@@ -27,9 +37,8 @@ class Tasks extends \Piwik\Plugin\Tasks
public function sendNotificationIfUpdatesAvailable()
{
- $updateCommunication = new UpdateCommunication();
- if ($updateCommunication->isEnabled()) {
- $updateCommunication->sendNotificationIfUpdatesAvailable();
+ if ($this->updateCommunication->isEnabled()) {
+ $this->updateCommunication->sendNotificationIfUpdatesAvailable();
}
}
diff --git a/plugins/CorePluginsAdmin/UpdateCommunication.php b/plugins/CorePluginsAdmin/UpdateCommunication.php
index 3ed004993f..da9f379324 100644
--- a/plugins/CorePluginsAdmin/UpdateCommunication.php
+++ b/plugins/CorePluginsAdmin/UpdateCommunication.php
@@ -12,6 +12,7 @@ use Piwik\Config;
use Piwik\Mail;
use Piwik\Option;
use Piwik\Piwik;
+use Piwik\Plugins\CoreUpdater\SystemSettings;
use Piwik\Plugins\UsersManager\API as UsersManagerApi;
use Piwik\SettingsPiwik;
@@ -20,7 +21,15 @@ use Piwik\SettingsPiwik;
*/
class UpdateCommunication
{
- private $enabledOptionName = 'enableUpdateCommunicationPlugins';
+ /**
+ * @var SystemSettings
+ */
+ private $updaterSettings;
+
+ public function __construct(SystemSettings $settings)
+ {
+ $this->updaterSettings = $settings;
+ }
/**
* Checks whether plugin update notification is enabled or not. If the marketplace is disabled or if update
@@ -30,13 +39,11 @@ class UpdateCommunication
*/
public function isEnabled()
{
- if (!$this->canBeEnabled()) {
+ if (!self::canBeEnabled()) {
return false;
}
- $isEnabled = Option::get($this->enabledOptionName);
-
- return !empty($isEnabled);
+ return $this->updaterSettings->sendPluginUpdateEmail->getValue();
}
/**
@@ -45,7 +52,7 @@ class UpdateCommunication
*
* @return bool
*/
- public function canBeEnabled()
+ public static function canBeEnabled()
{
$isEnabled = Config::getInstance()->General['enable_update_communication'];
@@ -53,22 +60,6 @@ class UpdateCommunication
}
/**
- * Enable plugin update notifications.
- */
- public function enable()
- {
- Option::set($this->enabledOptionName, 1);
- }
-
- /**
- * Disable plugin update notifications.
- */
- public function disable()
- {
- Option::set($this->enabledOptionName, 0);
- }
-
- /**
* Sends an email to all super users if there is an update available for any plugins from the Marketplace.
* For each update we send an email only once.
*
diff --git a/plugins/CorePluginsAdmin/Widgets/GetNewPlugins.php b/plugins/CorePluginsAdmin/Widgets/GetNewPlugins.php
new file mode 100644
index 0000000000..e19259c66a
--- /dev/null
+++ b/plugins/CorePluginsAdmin/Widgets/GetNewPlugins.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CorePluginsAdmin\Widgets;
+
+use Piwik\Common;
+use Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient;
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+use Piwik\View;
+
+class GetNewPlugins extends Widget
+{
+ /**
+ * @var MarketplaceApiClient
+ */
+ private $marketplaceApiClient;
+
+ public function __construct(MarketplaceApiClient $marketplaceApiClient)
+ {
+ $this->marketplaceApiClient = $marketplaceApiClient;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('About Piwik');
+ // TODO it actually shows "recently updated plugins currently". Need a new sort filter in the Marketplace
+ // TODO when decided whether to show new plugins or recently updated plugins add translation key
+ // we want to show new plugins likely (when changed Marketplace to support actually "newest" plugins)
+ $config->setName('Latest Piwik Plugin Updates');
+ $config->setOrder(19);
+ }
+
+ public function render()
+ {
+ $isAdminPage = Common::getRequestVar('isAdminPage', 0, 'int');
+
+ if (!empty($isAdminPage)) {
+ $template = 'getNewPluginsAdmin';
+ } else {
+ $template = 'getNewPlugins';
+ }
+
+ $plugins = $this->marketplaceApiClient->searchForPlugins('', '', 'newest');
+
+ return $this->renderTemplate($template, array(
+ 'plugins' => array_splice($plugins, 0, 3)
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/field/field.directive.html b/plugins/CorePluginsAdmin/angularjs/field/field.directive.html
new file mode 100644
index 0000000000..8e99065714
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/field/field.directive.html
@@ -0,0 +1,3 @@
+<div class="field">
+ {{ field.myProperty }}
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/field/field.directive.js b/plugins/CorePluginsAdmin/angularjs/field/field.directive.js
new file mode 100644
index 0000000000..8bfd41ebcf
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/field/field.directive.js
@@ -0,0 +1,141 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-field>
+ *
+ * eg <div piwik-field ui-control="select"
+ * title="{{ 'SitesManager_Timezone'|translate }}"
+ * value="site.timezone"
+ * options="timezones"
+ * inline-help="test"
+ * description=""
+ * introduction=""
+ * name=""
+ * autocomplete="off"
+ * disabled="true"
+ * full-width="true"
+ * templateFile=""></div>
+ *
+ * templateFile allows to render a custom template
+ * We do not use type= attribute here as it would match some CSS from input type=radio etc
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikField', piwikField);
+
+ piwikField.$inject = ['piwik', '$compile'];
+
+ function piwikField(piwik, $compile){
+
+ return {
+ restrict: 'A',
+ require: '?ngModel',
+ scope: {
+ uicontrol: '@',
+ name: '@',
+ value: '@',
+ default: '@',
+ options: '=',
+ description: '@',
+ introduction: '@',
+ title: '@',
+ inlineHelp: '@',
+ disabled: '=',
+ autocomplete: '@',
+ condition: '@',
+ varType: '@',
+ autofocus: '@',
+ tabindex: '@',
+ templateFile: '@',
+ fullWidth: '@',
+ maxlength: '@',
+ required: '@'
+ },
+ template: '<div piwik-form-field="field"></div>',
+ link: function(scope, elm, attrs, ctrl) {
+ if (!ctrl) {
+ return;
+ }
+
+ // load init value
+ if (scope.field.value !== undefined && scope.field.value !== null) {
+ ctrl.$setViewValue(scope.field.value);
+ } else if (ctrl.$viewValue) {
+ scope.field.value = ctrl.$viewValue;
+ }
+
+ // view -> model
+ scope.$watch('field.value', function (val, oldVal) {
+ if (val !== oldVal && val !== ctrl.$viewValue) {
+ ctrl.$setViewValue(val);
+ }
+ });
+
+ // model -> view
+ ctrl.$render = function() {
+ scope.field.value = ctrl.$viewValue;
+ };
+
+ },
+ controller: function ($scope) {
+ var field = {};
+ field.uiControl = $scope.uicontrol;
+ if ($scope.varType) {
+ field.type = $scope.varType;
+ } else if (field.uiControl === 'multiselect') {
+ field.type = 'array';
+ } else if (field.uiControl === 'checkbox') {
+ field.type = 'boolean';
+ } else if (field.uiControl === 'site') {
+ field.type = 'object';
+ } else {
+ field.type = 'string';
+ }
+
+ field.name = $scope.name;
+ field.value = $scope.value;
+ field.defaultValue = $scope.default;
+ field.availableValues = $scope.options;
+ field.description = $scope.description;
+ field.introduction = $scope.introduction;
+ field.inlineHelp = $scope.inlineHelp;
+ field.templateFile = $scope.templateFile;
+ field.title = $scope.title;
+ field.uiControlAttributes = {};
+ field.fullWidth = !!$scope.fullWidth;
+
+ if (field.type === 'array' && angular.isString(field.value) && field.value) {
+ field.value = JSON.parse(field.value);
+ }
+
+ var i = 0, attribute;
+ var attributes = ['disabled', 'autocomplete', 'tabindex', 'autofocus', 'required', 'maxlength'];
+ for (i; i < attributes.length; i++) {
+ attribute = attributes[i];
+ if (!!$scope[attribute]) {
+ field.uiControlAttributes[attribute] = $scope[attribute];
+ }
+ }
+
+ $scope.field = field;
+
+ $scope.$watch('options', function (val, oldVal) {
+ if (val !== oldVal) {
+ $scope.field.availableValues = val;
+ }
+ });
+
+ $scope.$watch('disabled', function (val, oldVal) {
+ if (val !== oldVal) {
+ $scope.field.uiControlAttributes.disabled = val;
+ }
+ });
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox-array.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox-array.html
new file mode 100644
index 0000000000..f50d98b507
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox-array.html
@@ -0,0 +1,16 @@
+<div>
+ <label class="fieldRadioTitle" ng-show="formField.title">{{ formField.title }}</label>
+ <p ng-repeat="checkboxModel in formField.availableOptions"
+ class="checkbox">
+ <input ng-model="formField.checkboxkeys[$index.toString()]"
+ value="{{ checkboxModel.key }}"
+ ng-change="formField.updateCheckboxArrayValue()"
+ piwik-attributes="{{formField.uiControlAttributes}}"
+ type="checkbox"
+ id="{{ formField.name + checkboxModel.key }}"
+ name="{{ checkboxModel.name }}">
+
+ <label for="{{ formField.name + checkboxModel.key }}">{{ checkboxModel.value }}
+ <span class="form-description" ng-show="checkboxModel.description">{{ checkboxModel.description }}</span></label>
+ </p>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox.html
new file mode 100644
index 0000000000..cc016afd67
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-checkbox.html
@@ -0,0 +1,10 @@
+<div class="checkbox">
+ <input ng-model="formField.value"
+ piwik-attributes="{{formField.uiControlAttributes}}"
+ ng-value="1"
+ type="checkbox"
+ id="{{ formField.name }}"
+ name="{{ formField.name }}">
+
+ <label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-file.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-file.html
new file mode 100644
index 0000000000..b784b4a91f
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-file.html
@@ -0,0 +1,10 @@
+<div>
+ <div class="btn">
+ <span for="{{ formField.name }}" ng-bind-html="formField.title"></span>
+ <input name="{{ formField.name }}" type="file" id="{{ formField.name }}">
+ </div>
+
+ <div class="file-path-wrapper">
+ <input class="file-path validate" ng-model="formField.value" type="text">
+ </div>
+</div>
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-hidden.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-hidden.html
new file mode 100644
index 0000000000..d7bb771124
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-hidden.html
@@ -0,0 +1,6 @@
+<div>
+ <input type="{{ formField.uiControl }}"
+ name="{{ formField.name }}"
+ ng-model="formField.value"
+ >
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html
new file mode 100644
index 0000000000..b6dd7bcd94
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html
@@ -0,0 +1,9 @@
+<div>
+ <select multiple
+ name="{{ formField.name }}"
+ ng-model="formField.value"
+ ng-options="t.key as t.value for t in formField.availableOptions"
+ piwik-attributes="{{formField.uiControlAttributes}}">
+ </select>
+ <label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html
new file mode 100644
index 0000000000..449d09621f
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html
@@ -0,0 +1,17 @@
+<div>
+ <label class="fieldRadioTitle" ng-show="formField.title">{{ formField.title }}</label>
+
+ <p ng-repeat="radioModel in formField.availableOptions"
+ class="radio">
+ <input ng-model="formField.value"
+ ng-value="radioModel.key"
+ type="radio"
+ id="{{ formField.name + radioModel.key }}"
+ name="{{ formField.name }}"
+ ng-disabled="radioModel.disabled"
+ piwik-attributes="{{formField.uiControlAttributes}}"
+ >
+ <label for="{{ formField.name + radioModel.key }}">{{ radioModel.value }}
+ <span class="form-description" ng-show="radioModel.description">{{ radioModel.description }}</span></label>
+ </p>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-select.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-select.html
new file mode 100644
index 0000000000..a9018bbf98
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-select.html
@@ -0,0 +1,8 @@
+<div>
+ <select name="{{ formField.name }}"
+ ng-model="formField.value"
+ ng-options="t.key as t.value group by t.group for t in formField.availableOptions"
+ piwik-attributes="{{formField.uiControlAttributes}}">
+ </select>
+ <label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
+</div>
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-site.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-site.html
new file mode 100644
index 0000000000..5fa647685e
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-site.html
@@ -0,0 +1,11 @@
+<div>
+ <label for="{{ formField.name }}" class="siteSelectorLabel" ng-bind-html="formField.title"></label>
+ <div piwik-siteselector
+ class="sites_autocomplete"
+ ng-model="formField.value"
+ id="{{ formField.name }}"
+ show-all-sites-item="false"
+ switch-site-on-select="false"
+ show-selected-site="true"
+ piwik-attributes="{{formField.uiControlAttributes}}"></div>
+</div>
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-text-array.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-text-array.html
new file mode 100644
index 0000000000..1ce85a2fc6
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-text-array.html
@@ -0,0 +1,10 @@
+<div>
+ <input class="control_{{ formField.uiControl }}"
+ type="{{ formField.uiControl }}"
+ name="{{ formField.name }}"
+ ng-list
+ ng-model="formField.value"
+ piwik-attributes="{{formField.uiControlAttributes}}"
+ >
+ <label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-text.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-text.html
new file mode 100644
index 0000000000..ac33824549
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-text.html
@@ -0,0 +1,12 @@
+<div>
+ <input
+ class="control_{{ formField.uiControl }}"
+ type="{{ formField.uiControl }}"
+ id="{{ formField.name }}"
+ name="{{ formField.name }}"
+ ng-model="formField.value"
+ ng-value="formField.value"
+ piwik-attributes="{{formField.uiControlAttributes}}"
+ >
+ <label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-textarea-array.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-textarea-array.html
new file mode 100644
index 0000000000..85b1df3564
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-textarea-array.html
@@ -0,0 +1,9 @@
+<div>
+ <textarea name="{{ formField.name }}"
+ ng-list="&#10;" ng-trim="false"
+ piwik-attributes="{{formField.uiControlAttributes}}"
+ ng-model="formField.value"
+ class="materialize-textarea"
+ ></textarea>
+ <label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-textarea.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-textarea.html
new file mode 100644
index 0000000000..d6336bb888
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-textarea.html
@@ -0,0 +1,8 @@
+<div>
+ <textarea name="{{ formField.name }}"
+ piwik-attributes="{{formField.uiControlAttributes}}"
+ ng-model="formField.value"
+ class="materialize-textarea"
+ ></textarea>
+ <label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html
index 3a3cf49605..7b2ce74430 100644
--- a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html
@@ -1,89 +1,28 @@
-<div class="form-group" ng-show="formField.showField">
- <p ng-if="formField.introduction" class="settingIntroduction">{{ formField.introduction }}</p>
+<div class="form-group row" ng-show="formField.showField">
- <label ng-if="formField.uiControl != 'checkbox'" ng-bind-html="formField.title"></label>
+ <h3 ng-if="formField.introduction" class="col s12">{{ formField.introduction }}</h3>
- <div class="form-help" ng-show="formField.inlineHelp">
- <span ng-bind-html="formField.inlineHelp"></span>
-
- <span ng-show="formField.defaultValue && formField.uiControl != 'checkbox' && formField.uiControl != 'radio'">
- <br />
- {{ 'General_Default'|translate }}:
- <span>{{formField.defaultValue|limitTo:50}}</span>
- </span>
- </div>
-
- <select ng-if="formField.uiControl == 'select'"
- name="{{ formField.name }}"
- ng-model="formField.value"
- ng-options="t.key as t.value group by t.group for t in formField.availableValues"
- piwik-attributes="{{formField.uiControlAttributes}}">
- </select>
-
- <select ng-if="formField.uiControl == 'multiselect'"
- multiple
- name="{{ formField.name }}"
- ng-model="formField.value"
- ng-options="t.key as t.value for t in formField.availableValues"
- piwik-attributes="{{formField.uiControlAttributes}}">
- </select>
-
- <textarea ng-if="formField.uiControl == 'textarea' && formField.type !== 'array'"
- name="{{ formField.name }}"
- piwik-attributes="{{formField.uiControlAttributes}}"
- ng-model="formField.value"
- ></textarea>
-
- <textarea ng-if="formField.uiControl == 'textarea' && formField.type === 'array'"
- name="{{ formField.name }}"
- ng-list="&#10;" ng-trim="false"
- piwik-attributes="{{formField.uiControlAttributes}}"
- ng-model="formField.value"
- ></textarea>
-
- <label ng-if="formField.uiControl == 'radio'"
- ng-repeat="(key,value) in formField.availableValues"
- class="radio">
- <input ng-model="formField.value"
- ng-value="key"
- type="radio"
- name="{{ formField.name }}"
- piwik-attributes="{{formField.uiControlAttributes}}"
- >
- {{ value }}
- <span ng-show="formField.description" class='form-description'>{{ formField.description }}</span>
- </label>
-
- <label ng-if="formField.uiControl == 'checkbox'" class="checkbox">
- <input ng-model="formField.value"
- piwik-attributes="{{formField.uiControlAttributes}}"
- ng-value="1"
- type="checkbox"
- name="{{ formField.name }}">
-
- <span ng-bind-html="formField.title"></span>
-
- <span ng-show="formField.description" class='form-description'>{{ formField.description }}</span>
- </label>
-
- <input ng-if="(formField.uiControl == 'text' || formField.uiControl == 'password') && formField.type !== 'array'"
- class="control_{{ formField.uiControl }}"
- type="{{ formField.uiControl }}"
- name="{{ formField.name }}"
- ng-model="formField.value"
- piwik-attributes="{{formField.uiControlAttributes}}"
- >
-
- <input ng-if="(formField.uiControl == 'text' || formField.uiControl == 'password') && formField.type === 'array'"
- class="control_{{ formField.uiControl }}"
- type="{{ formField.uiControl }}"
- name="{{ formField.name }}"
- ng-list
- ng-model="formField.value"
- piwik-attributes="{{formField.uiControlAttributes}}"
+ <div class="col s12"
+ ng-class="{'input-field': formField.uiControl != 'checkbox' && formField.uiControl != 'radio', 'file-field': formField.uiControl == 'file', 'm6': !formField.fullWidth}"
+ ng-include="formField.templateFile" onload="templateLoaded()"
>
- <span ng-show="formField.uiControl != 'checkbox' && formField.uiControl != 'radio'"
- class='form-description'>{{ formField.description }}</span>
-
+ </div>
+ <div class="col s12"
+ ng-class="{'m6': !formField.fullWidth}">
+ <div ng-if="formField.description || formField.inlineHelp || (formField.defaultValue && formField.uiControl != 'checkbox' && formField.uiControl != 'radio')"
+ class="form-help">
+
+ <div ng-show="formField.description"
+ class='form-description'>{{ formField.description }}</div>
+
+ <span class="inline-help" ng-bind-html="formField.inlineHelp"></span>
+
+ <span ng-show="formField.defaultValue && formField.uiControl != 'checkbox' && formField.uiControl != 'radio'">
+ <br />
+ {{ 'General_Default'|translate }}:
+ <span>{{formField.defaultValue|limitTo:50}}</span>
+ </span>
+ </div>
+ </div>
</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js
index b46067bf9d..76c6ff566d 100644
--- a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js
@@ -12,9 +12,146 @@
(function () {
angular.module('piwikApp').directive('piwikFormField', piwikFormField);
- piwikFormField.$inject = ['piwik'];
+ piwikFormField.$inject = ['piwik', '$timeout'];
- function piwikFormField(piwik){
+ function piwikFormField(piwik, $timeout){
+
+ function syncMultiCheckboxKeysWithFieldValue(field)
+ {
+ angular.forEach(field.availableOptions, function (option, index) {
+ if (option && field.value.indexOf(option.key) !== -1) {
+ field.checkboxkeys[index] = true;
+ } else {
+ field.checkboxkeys[index] = false;
+ }
+ });
+ }
+
+ function hasUiControl(field, uiControlType)
+ {
+ return field.uiControl === uiControlType;
+ }
+
+ function isSelectControl(field)
+ {
+ return hasUiControl(field, 'select') || hasUiControl(field, 'multiselect');
+ }
+
+ function isArrayCheckboxControl(field)
+ {
+ return field.type === 'array' && hasUiControl(field, 'checkbox');
+ }
+
+ function hasGroupedValues(availableValues)
+ {
+ if (!angular.isObject(availableValues)
+ || angular.isArray(availableValues)) {
+ return false;
+ }
+
+ var key;
+ for (key in availableValues) {
+ if (Object.prototype.hasOwnProperty.call(availableValues, key)) {
+ if (angular.isObject(availableValues[key])) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ function whenRendered(scope, element, inlineHelpNode) {
+ return function () {
+ var field = scope.formField;
+
+ if (inlineHelpNode) {
+ angular.element(inlineHelpNode).appendTo(element.find('.inline-help'));
+ }
+
+ if (isSelectControl(field)) {
+ var $select = element.find('select');
+ $select.material_select();
+
+ scope.$watch('formField.value', function (val, oldVal) {
+ if (val !== oldVal) {
+ $timeout(function () {
+ $select.material_select();
+ });
+ }
+ });
+
+ } else if (hasUiControl(field, 'textarea')) {
+ element.find('textarea').trigger('autoresize');
+ scope.$watch('formField.value', function (val, oldVal) {
+ if (val !== oldVal) {
+ $timeout(function () {
+ element.find('textarea').trigger('autoresize');
+ });
+ }
+ });
+
+ } else if (hasUiControl(field, 'file')) {
+
+ // angular doesn't support file input type with ngModel. We implement our own "two way binding"
+ var $file = element.find('[type=file]');
+
+ $file.on('change', function () {
+ scope.formField.value = $(this).val();
+ });
+
+ scope.$watch('formField.value', function (val, oldVal) {
+ if (val !== oldVal && val === '') {
+ $file.val('');
+ }
+ });
+
+ } else if (isArrayCheckboxControl(field)) {
+
+ Materialize.updateTextFields();
+
+ scope.$watch('formField.value', function (val, oldVal) {
+ if (val !== oldVal && val && !oldVal && angular.isArray(val)) {
+ // we do this only on initial check
+ syncMultiCheckboxKeysWithFieldValue(field);
+ }
+ });
+
+
+ } else if (hasUiControl(field, 'text')
+ || hasUiControl(field, 'textarea')
+ || hasUiControl(field, 'password')
+ || hasUiControl(field, 'email')
+ || hasUiControl(field, 'url')
+ || hasUiControl(field, 'search')) {
+ Materialize.updateTextFields();
+ scope.$watch('formField.value', function (val, oldVal) {
+ if (val !== oldVal) {
+ $timeout(function () {
+ Materialize.updateTextFields();
+ });
+ }
+ });
+ }
+ }
+ }
+
+ function getTemplate(field) {
+ var control = field.uiControl;
+ if (control === 'password') {
+ control = 'text'; // we use same template for text and password both
+ }
+
+ var file = 'field-' + control;
+ var fieldsSupportingArrays = ['textarea', 'checkbox', 'text'];
+ if (field.type === 'array' && fieldsSupportingArrays.indexOf(control) !== -1) {
+ file += '-array';
+ }
+
+ return 'plugins/CorePluginsAdmin/angularjs/form-field/' + file + '.html?cb=' + piwik.cacheBuster;
+ };
return {
restrict: 'A',
@@ -43,45 +180,33 @@
field.showField = scope.$eval(field.condition, values);
}
- function hasUiControl(field, uiControlType)
+ function formatAvailableValues(field)
{
- return field.uiControl === uiControlType;
- }
+ if (!field.availableValues) {
+ return;
+ }
- function isSelectControl(field)
- {
- return hasUiControl(field, 'select') || hasUiControl(field, 'multiselect');
- }
+ var flatValues = [];
- function hasGroupedValues(availableValues)
- {
- if (!angular.isObject(availableValues)
- || angular.isArray(availableValues)) {
- return false;
- }
+ if (hasUiControl(field, 'radio') || hasUiControl(field, 'checkbox')) {
+ angular.forEach(field.availableValues, function (value, key) {
- var key;
- for (key in availableValues) {
- if (Object.prototype.hasOwnProperty.call(availableValues, key)) {
- if (angular.isObject(availableValues[key])) {
- return true;
- } else {
- return false;
+ if (angular.isObject(value) && typeof value.key !== 'undefined'){
+ flatValues.push(value);
+ return;
}
- }
- }
- return false;
- }
+ if (field.type === 'integer' && angular.isString(key)) {
+ key = parseInt(key, 10);
+ }
- return function (scope, element, attrs) {
- var field = scope.piwikFormField;
-
- if (angular.isArray(field.defaultValue)) {
- field.defaultValue = field.defaultValue.join(',');
+ flatValues.push({key: key, value: value});
+ });
+
+ return flatValues;
}
- if (isSelectControl(field) && field.availableValues) {
+ if (isSelectControl(field)) {
var availableValues = field.availableValues;
if (!hasGroupedValues(availableValues)) {
@@ -92,6 +217,11 @@
angular.forEach(availableValues, function (values, group) {
angular.forEach(values, function (value, key) {
+ if (angular.isObject(value) && typeof value.key !== 'undefined'){
+ flatValues.push(value);
+ return;
+ }
+
if (field.type === 'integer' && angular.isString(key)) {
key = parseInt(key, 10);
}
@@ -100,11 +230,56 @@
});
});
- field.availableValues = flatValues;
+ return flatValues;
+ }
+
+ return field.availableValues;
+ }
+
+ return function (scope, element, attrs) {
+ var field = scope.piwikFormField;
+
+ if (angular.isArray(field.defaultValue)) {
+ field.defaultValue = field.defaultValue.join(',');
+ }
+
+ if (field.type === 'boolean') {
+ if (field.value && field.value > 0 && field.value !== '0') {
+ field.value = true;
+ } else {
+ field.value = false;
+ }
}
+ // we are setting availableOptions and not availableValues again. Otherwise when watching the scope
+ // availableValues and in the watch change availableValues could trigger lots of more watch events
+ field.availableOptions = formatAvailableValues(field);
+
field.showField = true;
+ var inlineHelpNode;
+ if (field.inlineHelp && field.inlineHelp.indexOf('#') === 0) {
+ inlineHelpNode = field.inlineHelp;
+ field.inlineHelp = ' '; // we make sure inline help will be shown
+ }
+
+ if (isArrayCheckboxControl(field)) {
+ field.updateCheckboxArrayValue = function () {
+ var values = [];
+ for (var x in field.checkboxkeys) {
+ if (field.checkboxkeys[x]) {
+ values.push(field.availableOptions[x].key);
+ }
+ }
+ field.value = values;
+ }
+ field.checkboxkeys = new Array(field.availableOptions.length);
+
+ if (field.value && angular.isArray(field.value)) {
+ syncMultiCheckboxKeysWithFieldValue(field);
+ }
+ }
+
if (field.condition && scope.allSettings) {
evaluateConditionalExpression(scope, field);
@@ -117,10 +292,29 @@
});
}
}
+ }
+ if (!field.templateFile) {
+ field.templateFile = getTemplate(field);
}
scope.formField = field;
+
+ scope.$watch('formField.availableValues', function (val, oldVal) {
+ if (val !== oldVal) {
+ scope.formField.availableOptions = formatAvailableValues(scope.formField);
+
+ if (isSelectControl(scope.formField)) {
+ $timeout(function () {
+ element.find('select').material_select();
+ });
+ }
+ }
+ });
+ scope.templateLoaded = function () {
+ $timeout(whenRendered(scope, element, inlineHelpNode));
+ };
+
};
}
};
diff --git a/plugins/CorePluginsAdmin/angularjs/form/form.directive.js b/plugins/CorePluginsAdmin/angularjs/form/form.directive.js
new file mode 100644
index 0000000000..1ae09df50d
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form/form.directive.js
@@ -0,0 +1,39 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-form>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikForm', piwikForm);
+
+ piwikForm.$inject = ['piwik', '$timeout'];
+
+ function piwikForm(piwik, $timeout){
+
+ return {
+ restrict: 'A',
+ priority: '10',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs) {
+
+ $timeout(function () {
+
+ element.find('input[type=text]').keypress(function (e) {
+ var key = e.keyCode || e.which;
+ if (key == 13) {
+ element.find('[piwik-save-button] input').triggerHandler('click');
+ }
+ });
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.controller.js b/plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.controller.js
new file mode 100644
index 0000000000..0f55e6da31
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.controller.js
@@ -0,0 +1,23 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('PiwikMarketplaceController', PiwikMarketplaceController);
+
+ PiwikMarketplaceController.$inject = ['piwik'];
+
+ function PiwikMarketplaceController(piwik) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ this.changePluginSort = function () {
+ piwik.broadcast.propagateNewPage('query=&sort=' + this.pluginSort);
+ };
+
+ this.changePluginType = function () {
+ piwik.broadcast.propagateNewPage('query=&show=' + this.pluginType);
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.directive.js b/plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.directive.js
new file mode 100644
index 0000000000..e528c03bd0
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/marketplace/marketplace.directive.js
@@ -0,0 +1,59 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-marketplace>
+ */
+(function () {
+
+ angular.module('piwikApp').directive('piwikMarketplace', piwikMarketplace);
+
+ piwikMarketplace.$inject = ['piwik', '$timeout'];
+
+ function piwikMarketplace(piwik, $timeout){
+
+ return {
+ restrict: 'A',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs) {
+
+ $timeout(function () {
+
+ $('.uploadPlugin').click(function (event) {
+ event.preventDefault();
+
+ piwikHelper.modalConfirm('#installPluginByUpload', {
+ yes: function () {
+ window.location = link;
+ }
+ });
+ });
+
+ $('#uploadPluginForm').submit(function (event) {
+
+ var $zipFile = $('[name=pluginZip]');
+ var fileName = $zipFile.val();
+
+ if (!fileName || '.zip' != fileName.slice(-4)) {
+ event.preventDefault();
+ alert(_pk_translate('CorePluginsAdmin_NoZipFileSelected'));
+ }
+ });
+
+ // Keeps the plugin descriptions the same height
+ $('.marketplace .plugin .description').dotdotdot({
+ after: 'a.more',
+ watch: 'window'
+ });
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js
index bbe3c02eb0..cbbccde247 100644
--- a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js
+++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js
@@ -29,7 +29,7 @@
self.isLoading = false;
});
- this.save = function () {
+ this.save = function (settings) {
var apiMethod = 'CorePluginsAdmin.setUserSettings';
if ($scope.mode === 'admin') {
apiMethod = 'CorePluginsAdmin.setSystemSettings';
@@ -38,22 +38,20 @@
this.isLoading = true;
var values = {};
- angular.forEach(this.settingsPerPlugin, function (settings) {
- if (!values[settings.pluginName]) {
- values[settings.pluginName] = [];
- }
+ if (!values[settings.pluginName]) {
+ values[settings.pluginName] = [];
+ }
- angular.forEach(settings.settings, function (setting) {
- var value = setting.value;
- if (value === false) {
- value = '0';
- } else if (value === true) {
- value = '1';
- }
- values[settings.pluginName].push({
- name: setting.name,
- value: value
- });
+ angular.forEach(settings.settings, function (setting) {
+ var value = setting.value;
+ if (value === false) {
+ value = '0';
+ } else if (value === true) {
+ value = '1';
+ }
+ values[settings.pluginName].push({
+ name: setting.name,
+ value: value
});
});
@@ -62,7 +60,10 @@
var UI = require('piwik/UI');
var notification = new UI.Notification();
- notification.show(_pk_translate('CoreAdminHome_PluginSettingsSaveSuccess'), {context: 'success'});
+ notification.show(_pk_translate('CoreAdminHome_PluginSettingsSaveSuccess'), {
+ id: 'generalSettings', context: 'success'
+ });
+ notification.scrollToNotification();
}, function () {
self.isLoading = false;
diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html
index 3748c3d458..bbd0a4f7a4 100644
--- a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html
+++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html
@@ -1,31 +1,25 @@
<div class="pluginSettings">
- <p>
- {{ 'CoreAdminHome_PluginSettingsIntro'|translate }}
+ <div ng-repeat="settings in pluginSettings.settingsPerPlugin"
+ class="card"
+ id="pluginSettings">
- <span ng-repeat="settings in pluginSettings.settingsPerPlugin">
- <a href="#{{ settings.pluginName }}">{{ settings.pluginName }}</a><span ng-hide="$last">, </span>
- </span>
- </p>
+ <div class="card-content">
+ <h2 id="{{ settings.pluginName }}" class="card-title">{{ settings.title }}</h2>
- <p ng-if="!pluginSettings.isLoading && pluginSettings.settingsPerPlugin.length === 0">
- {{ 'CorePluginsAdmin_NoPluginSettings'|translate }}
- </p>
+ <div ng-repeat="setting in settings.settings">
+ <div piwik-form-field="setting" all-settings="settings.settings"></div>
+ </div>
- <div piwik-activity-indicator loading="pluginSettings.isLoading"></div>
+ <input type="button" ng-click="pluginSettings.save(settings)"
+ ng-disabled="pluginSettings.isLoading"
+ value="{{ 'General_Save'|translate }}"
+ class="pluginsSettingsSubmit btn"/>
- <div ng-repeat="settings in pluginSettings.settingsPerPlugin"
- id="pluginSettings">
+ <div piwik-activity-indicator loading="pluginSettings.isLoading"></div>
- <h2 id="{{ settings.pluginName }}" class="secondary">{{ settings.pluginName }}</h2>
-
- <div ng-repeat="setting in settings.settings">
- <div piwik-form-field="setting" all-settings="settings.settings"></div>
</div>
- </div>
- <input type="button" ng-click="pluginSettings.save()"
- value="{{ 'General_Save'|translate }}"
- class="pluginsSettingsSubmit submit"/>
+ </div>
</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less
index cd747097ed..e991bf66d7 100644
--- a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less
+++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less
@@ -3,4 +3,7 @@
width: 376px;
height: 250px;
}
+}
+.pluginsSettingsSubmit {
+ margin-top: 30px;
} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/plugins/plugin-filter.directive.js b/plugins/CorePluginsAdmin/angularjs/plugins/plugin-filter.directive.js
new file mode 100644
index 0000000000..df77ee5d7f
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/plugins/plugin-filter.directive.js
@@ -0,0 +1,112 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-plugin-filter>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikPluginFilter', piwikPluginFilter);
+
+ piwikPluginFilter.$inject = ['piwik'];
+
+ function piwikPluginFilter(piwik){
+
+ return {
+ restrict: 'A',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs) {
+
+ updateAllNumbersOfMatchingPluginsInFilter();
+
+ function filterPlugins()
+ {
+ var filterOrigin = getCurrentFilterOrigin();
+ var filterStatus = getCurrentFilterStatus();
+
+ var $nodesToEnable = getMatchingNodes(filterOrigin, filterStatus);
+
+ $('#plugins tr[data-filter-origin][data-filter-status]').css('display', 'none');
+ $nodesToEnable.css('display', 'table-row');
+
+ updateAllNumbersOfMatchingPluginsInFilter();
+ }
+
+ function updateAllNumbersOfMatchingPluginsInFilter()
+ {
+ var filterOrigin = getCurrentFilterOrigin();
+ var filterStatus = getCurrentFilterStatus();
+
+ updateNumberOfMatchingPluginsInFilter('[data-filter-status="all"]', filterOrigin, 'all');
+ updateNumberOfMatchingPluginsInFilter('[data-filter-status="active"]', filterOrigin, 'active');
+ updateNumberOfMatchingPluginsInFilter('[data-filter-status="inactive"]', filterOrigin, 'inactive');
+
+ updateNumberOfMatchingPluginsInFilter('[data-filter-origin="all"]', 'all', filterStatus);
+ updateNumberOfMatchingPluginsInFilter('[data-filter-origin="core"]', 'core', filterStatus);
+ updateNumberOfMatchingPluginsInFilter('[data-filter-origin="noncore"]', 'noncore', filterStatus);
+ }
+
+ function updateNumberOfMatchingPluginsInFilter(selectorFilterToUpdate, filterOrigin, filterStatus)
+ {
+ var numMatchingNodes = getMatchingNodes(filterOrigin, filterStatus).length;
+ var updatedCounterText = ' (' + numMatchingNodes + ')';
+
+ element.find(selectorFilterToUpdate + ' .counter').text(updatedCounterText);
+ }
+
+ function getCurrentFilterOrigin()
+ {
+ return element.find('.origin a.active').data('filter-origin');
+ }
+
+ function getCurrentFilterStatus()
+ {
+ return element.find('.status a.active').data('filter-status');
+ }
+
+ function getMatchingNodes(filterOrigin, filterStatus)
+ {
+ var query = '#plugins tr';
+
+ if ('all' == filterOrigin) {
+ query += '[data-filter-origin]';
+ } else {
+ query += '[data-filter-origin=' + filterOrigin + ']';
+ }
+
+ if ('all' == filterStatus) {
+ query += '[data-filter-status]';
+ } else {
+ query += '[data-filter-status=' + filterStatus + ']';
+ }
+
+ return $(query);
+ }
+
+ element.find('.status').on('click', 'a', function (event) {
+ event.preventDefault();
+
+ $(this).siblings().removeClass('active');
+ $(this).addClass('active');
+
+ filterPlugins();
+ });
+
+ element.find('.origin').on('click', 'a', function (event) {
+ event.preventDefault();
+
+ $(this).siblings().removeClass('active');
+ $(this).addClass('active');
+
+ filterPlugins();
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/plugins/plugin-management.directive.js b/plugins/CorePluginsAdmin/angularjs/plugins/plugin-management.directive.js
new file mode 100644
index 0000000000..e09c1e3d28
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/plugins/plugin-management.directive.js
@@ -0,0 +1,85 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-plugin-management>
+ */
+(function () {
+
+ angular.module('piwikApp').directive('piwikPluginManagement', piwikPluginManagement);
+
+ piwikPluginManagement.$inject = ['piwik'];
+
+ function piwikPluginManagement(piwik){
+
+ return {
+ restrict: 'A',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs) {
+
+ var uninstallConfirmMessage = '';
+
+ element.find('.uninstall').click(function (event) {
+ event.preventDefault();
+
+ var link = $(this).attr('href');
+ var pluginName = $(this).attr('data-plugin-name');
+
+ if (!link || !pluginName) {
+ return;
+ }
+
+ if (!uninstallConfirmMessage) {
+ uninstallConfirmMessage = $('#uninstallPluginConfirm').text();
+ }
+
+ var messageToDisplay = uninstallConfirmMessage.replace('%s', pluginName);
+
+ $('#uninstallPluginConfirm').text(messageToDisplay);
+
+ piwikHelper.modalConfirm('#confirmUninstallPlugin', {
+ yes: function () {
+ window.location = link;
+ }
+ });
+ });
+
+ element.find('.plugin-donation-link').click(function (event) {
+ event.preventDefault();
+
+ var overlayId = $(this).data('overlay-id');
+
+ piwikHelper.modalConfirm('#'+overlayId, {});
+ });
+
+ $('.uploadPlugin').click(function (event) {
+ event.preventDefault();
+
+ piwikHelper.modalConfirm('#installPluginByUpload', {
+ yes: function () {
+ window.location = link;
+ }
+ });
+ });
+
+ $('#uploadPluginForm').submit(function (event) {
+
+ var $zipFile = $('[name=pluginZip]');
+ var fileName = $zipFile.val();
+
+ if (!fileName || '.zip' != fileName.slice(-4)) {
+ event.preventDefault();
+ alert(_pk_translate('CorePluginsAdmin_NoZipFileSelected'));
+ }
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/plugins/plugin-name.directive.js b/plugins/CorePluginsAdmin/angularjs/plugins/plugin-name.directive.js
new file mode 100644
index 0000000000..26c38c6443
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/plugins/plugin-name.directive.js
@@ -0,0 +1,65 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-plugin-name="MyPluginName" [data-activeplugintab="changelog"]>
+ */
+(function () {
+
+ broadcast.addPopoverHandler('browsePluginDetail', function (value) {
+ var pluginName = value;
+ var activeTab = null;
+
+ if (-1 !== value.indexOf('!')) {
+ activeTab = value.substr(value.indexOf('!') + 1);
+ pluginName = value.substr(0, value.indexOf('!'));
+ }
+
+ var url = 'module=CorePluginsAdmin&action=pluginDetails&pluginName=' + encodeURIComponent(pluginName);
+
+ if (activeTab) {
+ url += '&activeTab=' + encodeURIComponent(activeTab);
+ }
+
+ Piwik_Popover.createPopupAndLoadUrl(url, 'details');
+ });
+
+ angular.module('piwikApp').directive('piwikPluginName', piwikPluginName);
+
+ piwikPluginName.$inject = ['piwik'];
+
+ function piwikPluginName(piwik){
+
+ return {
+ restrict: 'A',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs) {
+
+ var pluginName = attrs.piwikPluginName;
+ var activeTab = attrs.activeplugintab;
+
+ if (!pluginName) {
+ return;
+ }
+
+ element.on('click', function (event) {
+ event.preventDefault();
+
+ if (activeTab) {
+ pluginName += '!' + activeTab;
+ }
+
+ broadcast.propagateNewPopoverParameter('browsePluginDetail', pluginName);
+ });
+
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.html b/plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.html
new file mode 100644
index 0000000000..61dc111280
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.html
@@ -0,0 +1,8 @@
+<div style="display:inline-block;">
+ <input type="button"
+ ng-click="onconfirm()"
+ value="{{ value ? value : ('General_Save'|translate) }}"
+ ng-disabled="saving || disabled"
+ class="btn"/>
+ <div piwik-activity-indicator loading="saving"></div>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.js b/plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.js
new file mode 100644
index 0000000000..d905d4e7ea
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.js
@@ -0,0 +1,31 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-save-button>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikSaveButton', piwikSaveButton);
+
+ piwikSaveButton.$inject = ['piwik'];
+
+ function piwikSaveButton(piwik){
+
+ return {
+ restrict: 'A',
+ replace: true,
+ scope: {
+ saving: '=?',
+ value: '@?',
+ disabled: '=?',
+ onconfirm: '&?'
+ },
+ templateUrl: 'plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.html?cb=' + piwik.cacheBuster
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/javascripts/marketplace.js b/plugins/CorePluginsAdmin/javascripts/marketplace.js
deleted file mode 100755
index ceb498a401..0000000000
--- a/plugins/CorePluginsAdmin/javascripts/marketplace.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-$(document).ready(function () {
-
- // Keeps the plugin descriptions the same height
- $('.marketplace .plugin .description').dotdotdot({
- after: 'a.more',
- watch: 'window'
- });
-
- $('a.plugin-details[data-pluginName]').on('click', function (event) {
- event.preventDefault();
-
- var pluginName = $(this).attr('data-pluginName');
- if (!pluginName) {
- return;
- }
-
- var activeTab = $(this).attr('data-activePluginTab');
- if (activeTab) {
- pluginName += '!' + activeTab;
- }
-
- broadcast.propagateNewPopoverParameter('browsePluginDetail', pluginName);
- });
-
- broadcast.addPopoverHandler('browsePluginDetail', function (value) {
- var pluginName = value;
- var activeTab = null;
-
- if (-1 !== value.indexOf('!')) {
- activeTab = value.substr(value.indexOf('!') + 1);
- pluginName = value.substr(0, value.indexOf('!'));
- }
-
- var url = 'module=CorePluginsAdmin&action=pluginDetails&pluginName=' + encodeURIComponent(pluginName);
-
- if (activeTab) {
- url += '&activeTab=' + encodeURIComponent(activeTab);
- }
-
- Piwik_Popover.createPopupAndLoadUrl(url, 'details');
- });
-
-});
diff --git a/plugins/CorePluginsAdmin/javascripts/pluginExtend.js b/plugins/CorePluginsAdmin/javascripts/pluginExtend.js
deleted file mode 100644
index 63af1f8433..0000000000
--- a/plugins/CorePluginsAdmin/javascripts/pluginExtend.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-$(document).ready(function () {
-
- $('.uploadPlugin').click(function (event) {
- event.preventDefault();
-
- piwikHelper.modalConfirm('#installPluginByUpload', {
- yes: function () {
- window.location = link;
- }
- });
- });
-
- $('#uploadPluginForm').submit(function (event) {
-
- var $zipFile = $('[name=pluginZip]');
- var fileName = $zipFile.val();
-
- if (!fileName || '.zip' != fileName.slice(-4)) {
- event.preventDefault();
- alert(_pk_translate('CorePluginsAdmin_NoZipFileSelected'));
- }
- });
-
-}); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/javascripts/pluginOverview.js b/plugins/CorePluginsAdmin/javascripts/pluginOverview.js
deleted file mode 100644
index 748ed9853e..0000000000
--- a/plugins/CorePluginsAdmin/javascripts/pluginOverview.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-$(document).ready(function () {
-
- var uninstallConfirmMessage = '';
-
- $('#plugins .uninstall').click(function (event) {
- event.preventDefault();
-
- var link = $(this).attr('href');
- var pluginName = $(this).attr('data-pluginName');
-
- if (!link || !pluginName) {
- return;
- }
-
- if (!uninstallConfirmMessage) {
- uninstallConfirmMessage = $('#uninstallPluginConfirm').text();
- }
-
- var messageToDisplay = uninstallConfirmMessage.replace('%s', pluginName);
-
- $('#uninstallPluginConfirm').text(messageToDisplay);
-
- piwikHelper.modalConfirm('#confirmUninstallPlugin', {
- yes: function () {
- window.location = link;
- }
- });
- });
-
- $('.plugin-donation-link').click(function (event) {
- event.preventDefault();
-
- var overlayId = $(this).data('overlay-id');
-
- piwikHelper.modalConfirm('#'+overlayId, {});
- });
-
-}); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/javascripts/plugins.js b/plugins/CorePluginsAdmin/javascripts/plugins.js
deleted file mode 100644
index 0da39fb8fc..0000000000
--- a/plugins/CorePluginsAdmin/javascripts/plugins.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-$(document).ready(function () {
-
- updateAllNumbersOfMatchingPluginsInFilter();
-
- function filterPlugins()
- {
- var filterOrigin = getCurrentFilterOrigin();
- var filterStatus = getCurrentFilterStatus();
-
- var $nodesToEnable = getMatchingNodes(filterOrigin, filterStatus);
-
- $('#plugins tr[data-filter-origin][data-filter-status]').css('display', 'none');
- $nodesToEnable.css('display', 'table-row');
-
- updateAllNumbersOfMatchingPluginsInFilter();
- }
-
- function updateAllNumbersOfMatchingPluginsInFilter()
- {
- var filterOrigin = getCurrentFilterOrigin();
- var filterStatus = getCurrentFilterStatus();
-
- updateNumberOfMatchingPluginsInFilter('[data-filter-status="all"]', filterOrigin, 'all');
- updateNumberOfMatchingPluginsInFilter('[data-filter-status="active"]', filterOrigin, 'active');
- updateNumberOfMatchingPluginsInFilter('[data-filter-status="inactive"]', filterOrigin, 'inactive');
-
- updateNumberOfMatchingPluginsInFilter('[data-filter-origin="all"]', 'all', filterStatus);
- updateNumberOfMatchingPluginsInFilter('[data-filter-origin="core"]', 'core', filterStatus);
- updateNumberOfMatchingPluginsInFilter('[data-filter-origin="noncore"]', 'noncore', filterStatus);
- }
-
- function updateNumberOfMatchingPluginsInFilter(selectorFilterToUpdate, filterOrigin, filterStatus)
- {
- var numMatchingNodes = getMatchingNodes(filterOrigin, filterStatus).length;
- var updatedCounterText = ' (' + numMatchingNodes + ')';
-
- $('.pluginsFilter ' + selectorFilterToUpdate + ' .counter').text(updatedCounterText);
- }
-
- function getCurrentFilterOrigin()
- {
- return $('.pluginsFilter .origin a.active').data('filter-origin');
- }
-
- function getCurrentFilterStatus()
- {
- return $('.pluginsFilter .status a.active').data('filter-status');
- }
-
- function getMatchingNodes(filterOrigin, filterStatus)
- {
- var query = '#plugins tr';
-
- if ('all' == filterOrigin) {
- query += '[data-filter-origin]';
- } else {
- query += '[data-filter-origin=' + filterOrigin + ']';
- }
-
- if ('all' == filterStatus) {
- query += '[data-filter-status]';
- } else {
- query += '[data-filter-status=' + filterStatus + ']';
- }
-
- return $(query);
- }
-
- $('.pluginsFilter .status').on('click', 'a', function (event) {
- event.preventDefault();
-
- $(this).siblings().removeClass('active');
- $(this).addClass('active');
-
- filterPlugins();
- });
-
- $('.pluginsFilter .origin').on('click', 'a', function (event) {
- event.preventDefault();
-
- $(this).siblings().removeClass('active');
- $(this).addClass('active');
-
- filterPlugins();
- });
-}); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/lang/en.json b/plugins/CorePluginsAdmin/lang/en.json
index 10aa1f8f00..89157cdaae 100644
--- a/plugins/CorePluginsAdmin/lang/en.json
+++ b/plugins/CorePluginsAdmin/lang/en.json
@@ -35,6 +35,8 @@
"InstallingPlugin": "Installing %s",
"InstallNewPlugins": "Install new plugins",
"InstallNewThemes": "Install new themes",
+ "InstalledPlugins": "Installed plugins",
+ "InstalledThemes": "Installed themes",
"LastCommitTime": "(last commit %s)",
"LastUpdated": "Last Updated",
"LicenseHomepage": "License Homepage",
@@ -71,9 +73,9 @@
"PluginVersionInfo": "%1$s from %2$s",
"PluginWebsite": "Plugin Website",
"Screenshots": "Screenshots",
- "SortByAlpha": "alpha",
- "SortByNewest": "newest",
- "SortByPopular": "popular",
+ "SortByAlpha": "Alpha",
+ "SortByNewest": "Newest",
+ "SortByPopular": "Popular",
"Status": "Status",
"StepDownloadingPluginFromMarketplace": "Downloading plugin from Marketplace",
"StepDownloadingThemeFromMarketplace": "Downloading theme from Marketplace",
@@ -88,8 +90,8 @@
"SuccessfullyActicated": "You have successfully activated <strong>%s<\/strong>.",
"Support": "Support",
"TeaserExtendPiwik": "Extend Piwik with Plugins and Themes",
- "TeaserExtendPiwikByPlugin": "Extend Piwik by installing a new plugin",
- "TeaserExtendPiwikByTheme": "Enjoy another look & feel by installing a new theme",
+ "TeaserExtendPiwikByPlugin": "Extend Piwik by %1$sinstalling a new plugin%2$s.",
+ "TeaserExtendPiwikByTheme": "Enjoy another look & feel by %1$sinstalling a new theme%2$s.",
"TeaserExtendPiwikByUpload": "Extend Piwik by uploading a ZIP file",
"InstallingNewPluginViaMarketplaceOrUpload": "You may automatically install plugins from the Marketplace or %1$supload a plugin%2$s in .zip format.",
"Theme": "Theme",
@@ -102,6 +104,7 @@
"UploadZipFile": "Upload ZIP file",
"Version": "Version",
"ViewRepositoryChangelog": "View the changes",
+ "ViewAllMarketplacePlugins": "View all Marketplace plugins",
"Websites": "Websites"
}
} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/stylesheets/marketplace-widget.less b/plugins/CorePluginsAdmin/stylesheets/marketplace-widget.less
new file mode 100644
index 0000000000..3438983825
--- /dev/null
+++ b/plugins/CorePluginsAdmin/stylesheets/marketplace-widget.less
@@ -0,0 +1,44 @@
+.uiTest .getNewPlugins {
+ .row {
+ display: none;
+ }
+}
+
+.getNewPlugins {
+ .pluginName {
+ cursor: pointer;
+ }
+
+ .widgetBody {
+ margin-bottom: 20px;
+ }
+
+ .screenshot {
+ cursor: pointer;
+ }
+
+ &:not(.isAdminPage) {
+ h3 {
+ padding-left: 0 !important;
+ padding-top: 5px !important;
+ }
+ }
+
+ &.isAdminPage {
+ h3 {
+ margin-top:0;
+ margin-bottom: 8px;
+ }
+ .description {
+ height: 30px;
+ }
+ .row {
+ margin-left: -12px;
+ margin-right: -12px;
+ }
+ .screenshot {
+ max-height: 250px;
+ object-fit: cover;
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/stylesheets/marketplace.less b/plugins/CorePluginsAdmin/stylesheets/marketplace.less
index fd6442a9d2..3c1371ffb8 100644
--- a/plugins/CorePluginsAdmin/stylesheets/marketplace.less
+++ b/plugins/CorePluginsAdmin/stylesheets/marketplace.less
@@ -1,21 +1,29 @@
.marketplace {
- .plugin-search {
- float: right;
+ > .row {
+ margin: 0 -0.75rem;
+ }
- input, button {
- height: 41px;
- margin-bottom: 0;
- }
- button {
- font-size: inherit !important; // because the default Piwik button style is crazy
- }
+ .marketplaceActions {
+ margin-bottom: 0;
}
- .marketplace-max-width {
- max-width: 980px;
+ .plugin-search {
+ position: relative;
+ margin-right: 20px;
+ .icon-search {
+ position: absolute;
+ bottom: 32px;
+ right: -16px;
+ font-size: 16px;
+ cursor: pointer;
+ }
+
}
.plugin {
+ h3 {
+ word-break: break-all;
+ }
text-align: center;
.description {
@line-height: 18px;
@@ -42,6 +50,7 @@
overflow-x: hidden;
white-space: nowrap;
line-height: 18px;
+ font-size: 13px;
}
.update-available {
// Code taken from Bootstrap's labels
@@ -59,7 +68,7 @@
text-decoration: none;
}
}
- .panel-footer {
+ .footer {
padding: 12px 40px;
}
}
diff --git a/plugins/CorePluginsAdmin/stylesheets/plugin-details.less b/plugins/CorePluginsAdmin/stylesheets/plugin-details.less
index c447e35025..1dee1eb224 100644
--- a/plugins/CorePluginsAdmin/stylesheets/plugin-details.less
+++ b/plugins/CorePluginsAdmin/stylesheets/plugin-details.less
@@ -3,25 +3,44 @@
text-align: left;
line-height: 20px;
+ .header {
+ .intro {
+ width:75%;
+ float:left;
+ margin-bottom: 8px;
+ }
+ .actionButton {
+ width:25%;
+ float:left;
+ }
+ }
+
+ #pluginDetailsTabs > .col {
+ padding: 0 16px 0 0;
+ }
+
+ .content {
+ .contentDetails {
+ width:75%;
+ float:left;
+ }
+ .metadata {
+ width:25%;
+ float:left;
+ padding-top: 16px;
+ }
+ }
+
h3, h4, h5, h6 {
margin: 20px 0px 10px 0px;
color: #000000;
}
- .ui-tabs-panel ul, .ui-tabs-panel ol {
- list-style: initial;
- padding-left: 20px;
- }
-
- p, .ui-tabs-panel ul, .ui-tabs-panel li {
+ p {
text-align: left;
line-height: 20px;
}
- .header .intro {
- margin-bottom: 15px;
- }
-
.content p {
margin: 0 0 10px;
}
@@ -30,43 +49,6 @@
padding-right: 25px;
}
- .ui-tabs {
- padding: 0em;
- }
-
- .ui-tabs .ui-tabs-nav {
- padding: 0em;
- border-bottom: 1px solid #cccccc;
- margin-right: 25px;
- border-radius: 0px;
- font-size: 15px;
- }
-
- .ui-tabs .ui-tabs-panel {
- padding: 1.4em 3em 0em 0em;
- }
-
- .content a {
- color: @theme-color-link;
- text-decoration: none;
- }
-
- .ui-state-default {
- border: 0px !important;
- }
-
- .ui-state-active {
- padding-bottom: 0px !important;
- }
-
- .ui-state-active.ui-state-default {
- border: 1px solid #cccccc !important;
- }
-
- .ui-state-default:hover {
- background-color: #eeeeee !important;
- }
-
.install {
padding: 11px 19px;
font-size: 17.5px;
@@ -84,12 +66,12 @@
}
dt {
- font-weight: bold;
line-height: 20px;
}
dd {
margin-left: 10px;
line-height: 20px;
+ color: @theme-color-text-contrast;
}
.featuredIcon {
diff --git a/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less b/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less
index 4d43e0e600..0be9d31c7f 100644
--- a/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less
+++ b/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less
@@ -1,22 +1,3 @@
-.pluginsManagement .entityContainer {
- padding-top: 16px;
-}
-
-table.dataTable tr.active-plugin > td {
- background-color:@theme-color-background-base !important;
-}
-
-table.dataTable tr.active-plugin:hover > td {
- background-color:@theme-color-background-base !important;
-}
-
-table.dataTable tr.inactive-plugin > td {
- background-color:#ddd !important;
-}
-
-table.dataTable tr.inactive-plugin:hover > td {
- background-color:#ddd !important;
-}
.plugin-desc-text {
margin-top:0em;
@@ -86,9 +67,7 @@ table.entityTable tr td a.uninstall {
}
.plugin-version {
- font-size:80%;
- font-style:italic;
- color:#777;
+ color: #999;
}
#plugins {
@@ -101,6 +80,10 @@ table.entityTable tr td a.uninstall {
}
}
+ td.vers {
+ white-space: nowrap;
+ }
+
.plugin-desc-missingrequirements {
font-weight:bold;
font-style: italic;
@@ -119,7 +102,8 @@ table.entityTable tr td a.uninstall {
}
.admin .pluginsFilter {
- color: @theme-color-text-lighter;
+ margin-top: 20px;
+
.active {
font-weight: bold;
}
@@ -133,8 +117,4 @@ table.entityTable tr td a.uninstall {
display: inline-block;
margin-left: 20px;
}
-
- .getNewPlugins {
- float: right;
- }
}
diff --git a/plugins/CorePluginsAdmin/templates/getNewPlugins.twig b/plugins/CorePluginsAdmin/templates/getNewPlugins.twig
new file mode 100644
index 0000000000..0c68e552e3
--- /dev/null
+++ b/plugins/CorePluginsAdmin/templates/getNewPlugins.twig
@@ -0,0 +1,22 @@
+<div class="getNewPlugins">
+ <div class="row">
+ {% for plugin in plugins %}
+ <div class="col s12">
+
+ <h3 class="pluginName" piwik-plugin-name="{{ plugin.name|e('html_attr') }}">{{ plugin.name }}</h3>
+ <span>
+ {{ plugin.description }}
+ <br />
+ <a href="javascript:;" piwik-plugin-name="{{ plugin.name|e('html_attr') }}">{{ 'General_MoreDetails'|translate }}</a>
+ </span>
+
+ {% if not loop.last %}<br /><br />{% endif %}
+ </div>
+ {% endfor %}
+ </div>
+
+ <div class="widgetBody">
+ <a href="{{ linkTo({'module': 'CorePluginsAdmin', 'action': 'marketplace'}) }}"
+ >{{ 'CorePluginsAdmin_ViewAllMarketplacePlugins'|translate }}</a>
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/templates/getNewPluginsAdmin.twig b/plugins/CorePluginsAdmin/templates/getNewPluginsAdmin.twig
new file mode 100644
index 0000000000..b929a48430
--- /dev/null
+++ b/plugins/CorePluginsAdmin/templates/getNewPluginsAdmin.twig
@@ -0,0 +1,32 @@
+<div class="getNewPlugins isAdminPage">
+ <div class="row">
+ {% for plugin in plugins %}
+ <div class="col s12 m4">
+
+ <h3 class="pluginName"
+ title="{{ plugin.description|e('html_attr') }}"
+ piwik-plugin-name="{{ plugin.name|e('html_attr') }}">{{ plugin.name }}</h3>
+ <p class="description"
+ title="{{ plugin.description|e('html_attr') }}">{{ plugin.description }}</p>
+
+ {% if plugin.screenshots and plugin.screenshots.0 is defined %}
+ <br />
+ <img piwik-plugin-name="{{ plugin.name|e('html_attr') }}"
+ class="screenshot"
+ src="{{ plugin.screenshots.0 }}?w=600" width="100%" alt="">
+ {% endif %}
+ </div>
+ {% endfor %}
+ </div>
+
+ <div class="widgetBody">
+ <a href="{{ linkTo({'module': 'CorePluginsAdmin', 'action': 'marketplace'}) }}"
+ >{{ 'CorePluginsAdmin_ViewAllMarketplacePlugins'|translate }}</a>
+ </div>
+
+ <script type="text/javascript">
+ $('.getNewPlugins .col .description').dotdotdot({
+ watch: 'window'
+ });
+ </script>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/templates/macros.twig b/plugins/CorePluginsAdmin/templates/macros.twig
index c64500c518..5a855c20d6 100644
--- a/plugins/CorePluginsAdmin/templates/macros.twig
+++ b/plugins/CorePluginsAdmin/templates/macros.twig
@@ -1,54 +1,52 @@
{% macro tablePluginUpdates(pluginsHavingUpdate, nonce, isTheme) %}
- <div class='entityContainer'>
- <table class="dataTable entityTable">
- <thead>
- <tr>
- <th>{% if isTheme %}{{ 'CorePluginsAdmin_Theme'|translate }}{% else %}{{ 'General_Plugin'|translate }}{% endif %}</th>
- <th class="num">{{ 'CorePluginsAdmin_Version'|translate }}</th>
- <th>{{ 'General_Description'|translate }}</th>
- <th class="status">{{ 'CorePluginsAdmin_Status'|translate }}</th>
- <th class="action-links">{{ 'General_Action'|translate }}</th>
+ <table piwik-content-table>
+ <thead>
+ <tr>
+ <th>{% if isTheme %}{{ 'CorePluginsAdmin_Theme'|translate }}{% else %}{{ 'General_Plugin'|translate }}{% endif %}</th>
+ <th class="num">{{ 'CorePluginsAdmin_Version'|translate }}</th>
+ <th>{{ 'General_Description'|translate }}</th>
+ <th class="status">{{ 'CorePluginsAdmin_Status'|translate }}</th>
+ <th class="action-links">{{ 'General_Action'|translate }}</th>
+ </tr>
+ </thead>
+ <tbody id="plugins">
+ {% for name,plugin in pluginsHavingUpdate %}
+ <tr {% if plugin.isActivated|default(false) %}class="active-plugin"{% else %}class="inactive-plugin"{% endif %}>
+ <td class="name">
+ <a href="javascript:void(0);" piwik-plugin-name="{{ plugin.name|e('html_attr') }}" class="plugin-details">
+ {{ plugin.name }}
+ </a>
+ </td>
+ <td class="vers">
+ {% if plugin.repositoryChangelogUrl %}
+ <a href="javascript:void(0);" title="{{ 'CorePluginsAdmin_Changelog'|translate }}" data-activePluginTab="changelog" piwik-plugin-name="{{ plugin.name|e('html_attr') }}">{{ plugin.currentVersion }} => {{ plugin.latestVersion }}</a>
+ {% else %}
+ {{ plugin.currentVersion }} => {{ plugin.latestVersion }}
+ {% endif %}
+ </td>
+ <td class="desc">
+ {{ plugin.description }}
+ {{ _self.missingRequirementsPleaseUpdateNotice(plugin) }}
+ </td>
+ <td class="status">
+ {% if plugin.isActivated %}
+ {{ 'CorePluginsAdmin_Active'|translate }}
+ {% else %}
+ {{ 'CorePluginsAdmin_Inactive'|translate }}
+ {% endif %}
+ </td>
+ <td class="togl action-links">
+ {% if 0 == plugin.missingRequirements|length %}
+ <a href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': nonce}) }}">Update</a>
+ {% else %}
+ -
+ {% endif %}
+ </td>
</tr>
- </thead>
- <tbody id="plugins">
- {% for name,plugin in pluginsHavingUpdate %}
- <tr {% if plugin.isActivated|default(false) %}class="active-plugin"{% else %}class="inactive-plugin"{% endif %}>
- <td class="name">
- <a href="javascript:void(0);" data-pluginName="{{ plugin.name|e('html_attr') }}" class="plugin-details">
- {{ plugin.name }}
- </a>
- </td>
- <td class="vers">
- {% if plugin.repositoryChangelogUrl %}
- <a href="javascript:void(0);" title="{{ 'CorePluginsAdmin_Changelog'|translate }}" class="plugin-details" data-activePluginTab="changelog" data-pluginName="{{ plugin.name|e('html_attr') }}">{{ plugin.currentVersion }} => {{ plugin.latestVersion }}</a>
- {% else %}
- {{ plugin.currentVersion }} => {{ plugin.latestVersion }}
- {% endif %}
- </td>
- <td class="desc">
- {{ plugin.description }}
- {{ _self.missingRequirementsPleaseUpdateNotice(plugin) }}
- </td>
- <td class="status">
- {% if plugin.isActivated %}
- {{ 'CorePluginsAdmin_Active'|translate }}
- {% else %}
- {{ 'CorePluginsAdmin_Inactive'|translate }}
- {% endif %}
- </td>
- <td class="togl action-links">
- {% if 0 == plugin.missingRequirements|length %}
- <a href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': nonce}) }}">Update</a>
- {% else %}
- -
- {% endif %}
- </td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- </div>
+ {% endfor %}
+ </tbody>
+ </table>
{% endmacro %}
@@ -63,9 +61,9 @@
align="{{ align }}" />
{% endmacro %}
-{% macro pluginsFilter(isTheme, isMarketplaceEnabled) %}
+{% macro pluginsFilter() %}
- <p class="pluginsFilter entityContainer">
+ <p class="row pluginsFilter" piwik-plugin-filter>
<span class="origin">
<strong>{{ 'CorePluginsAdmin_Origin'|translate }}</strong>
<a data-filter-origin="all" href="#" class="active">{{ 'General_All'|translate }}<span class="counter"></span></a> |
@@ -79,16 +77,6 @@
<a data-filter-status="active" href="#">{{ 'CorePluginsAdmin_Active'|translate }}<span class="counter"></span></a> |
<a data-filter-status="inactive" href="#">{{ 'CorePluginsAdmin_Inactive'|translate }}<span class="counter"></span></a>
</span>
-
- {% if isMarketplaceEnabled %}
- <span class="getNewPlugins">
- {% if isTheme %}
- <a href="{{ linkTo({'action':'browseThemes', 'sort': ''}) }}">{{ 'CorePluginsAdmin_InstallNewThemes'|translate }}</a>
- {% else %}
- <a href="{{ linkTo({'action':'browsePlugins', 'sort': ''}) }}">{{ 'CorePluginsAdmin_InstallNewPlugins'|translate }}</a>
- {% endif %}
- </span>
- {% endif %}
</p>
{% endmacro %}
@@ -136,145 +124,151 @@
</div>
-<div class='entityContainer'>
- <table class="dataTable entityTable">
- <thead>
- <tr>
- <th>{% if isTheme %}{{ 'CorePluginsAdmin_Theme'|translate }}{% else %}{{ 'General_Plugin'|translate }}{% endif %}</th>
- <th>{{ 'General_Description'|translate }}</th>
- <th class="status">{{ 'CorePluginsAdmin_Status'|translate }}</th>
- {% if (displayAdminLinks) %}
- <th class="action-links">{{ 'General_Action'|translate }}</th>
- {% endif %}
- </tr>
- </thead>
- <tbody id="plugins">
- {% for name,plugin in pluginsInfo %}
- {% set isDefaultTheme = isTheme and name == 'Morpheus' %}
- {% if (plugin.alwaysActivated is defined and not plugin.alwaysActivated) or isTheme %}
- <tr {% if plugin.activated %}class="active-plugin"{% else %}class="inactive-plugin"{% endif %} data-filter-status="{% if plugin.activated %}active{% else %}inactive{% endif %}" data-filter-origin="{% if plugin.isCorePlugin %}core{% else %}noncore{% endif %}">
- <td class="name">
- <a name="{{ name|e('html_attr') }}"></a>
- {% if not plugin.isCorePlugin and name in marketplacePluginNames -%}
- <a href="javascript:void(0);" class="plugin-details"
- data-pluginName="{{ name|e('html_attr') }}"
- >{{ name }}</a>
- {%- else %}
- {{ name }}
- {% endif %}
- <span class="plugin-version" {% if plugin.isCorePlugin %}title="{{ 'CorePluginsAdmin_CorePluginTooltip'|translate }}"{% endif %}>({% if plugin.isCorePlugin %}{{ 'CorePluginsAdmin_OriginCore'|translate }}{% else %}v{{ plugin.info.version }}{% endif %})</span>
+<table piwik-content-table>
+ <thead>
+ <tr>
+ <th>{% if isTheme %}{{ 'CorePluginsAdmin_Theme'|translate }}{% else %}{{ 'General_Plugin'|translate }}{% endif %}</th>
+ <th>{{ 'General_Description'|translate }}</th>
+ <th class="status">{{ 'CorePluginsAdmin_Status'|translate }}</th>
+ {% if (displayAdminLinks) %}
+ <th class="action-links">{{ 'General_Action'|translate }}</th>
+ {% endif %}
+ </tr>
+ </thead>
+ <tbody id="plugins">
+ {% for name,plugin in pluginsInfo %}
+ {% set isDefaultTheme = isTheme and name == 'Morpheus' %}
+ {% if (plugin.alwaysActivated is defined and not plugin.alwaysActivated) or isTheme %}
+ <tr {% if plugin.activated %}class="active-plugin"{% else %}class="inactive-plugin"{% endif %} data-filter-status="{% if plugin.activated %}active{% else %}inactive{% endif %}" data-filter-origin="{% if plugin.isCorePlugin %}core{% else %}noncore{% endif %}">
+ <td class="name">
+ <a name="{{ name|e('html_attr') }}"></a>
+ {% if not plugin.isCorePlugin and name in marketplacePluginNames -%}
+ <a href="javascript:void(0);"
+ piwik-plugin-name="{{ name|e('html_attr') }}"
+ >{{ name }}</a>
+ {%- else %}
+ {{ name }}
+ {% endif %}
+ <span class="plugin-version" {% if plugin.isCorePlugin %}title="{{ 'CorePluginsAdmin_CorePluginTooltip'|translate }}"{% endif %}>({% if plugin.isCorePlugin %}{{ 'CorePluginsAdmin_OriginCore'|translate }}{% else %}v{{ plugin.info.version }}{% endif %})</span>
- {% if name in pluginNamesHavingSettings %}
- <br /><br />
- <a href="{{ linkTo({'module':'CoreAdminHome', 'action': 'generalSettings'}) }}#{{ name|e('html_attr') }}" class="settingsLink">{{ 'General_Settings'|translate }}</a>
+ {% if name in pluginNamesHavingSettings %}
+ <br /><br />
+ <a href="{{ linkTo({'module':'CoreAdminHome', 'action': 'generalSettings'}) }}#{{ name|e('html_attr') }}" class="settingsLink">{{ 'General_Settings'|translate }}</a>
+ {% endif %}
+ </td>
+ <td class="desc">
+ <div class="plugin-desc-missingrequirements">
+ {% if plugin.missingRequirements is defined and plugin.missingRequirements %}
+ {{ _self.missingRequirementsInfo(name, plugin.info, plugin.missingRequirements, marketplacePluginNames) }}
+ <br />
{% endif %}
- </td>
- <td class="desc">
- <div class="plugin-desc-missingrequirements">
- {% if plugin.missingRequirements is defined and plugin.missingRequirements %}
- {{ _self.missingRequirementsInfo(name, plugin.info, plugin.missingRequirements, marketplacePluginNames) }}
- <br />
- {% endif %}
- </div>
- <div class="plugin-desc-text">
+ </div>
+ <div class="plugin-desc-text">
- {{ plugin.info.description|raw|nl2br }}
+ {{ plugin.info.description|raw|nl2br }}
- {% if plugin.info.homepage|default is not empty and plugin.info.homepage not in [
- 'http://piwik.org', 'http://www.piwik.org', 'http://piwik.org/', 'http://www.piwik.org/'
- ] %}
- <span class="plugin-homepage">
- <a target="_blank" href="{{ plugin.info.homepage }}">({{ 'CorePluginsAdmin_PluginHomepage'|translate|replace({' ': '&nbsp;'})|raw }})</a>
- </span>
- {% endif %}
+ {% if plugin.info.homepage|default is not empty and plugin.info.homepage not in [
+ 'http://piwik.org', 'http://www.piwik.org', 'http://piwik.org/', 'http://www.piwik.org/'
+ ] %}
+ <span class="plugin-homepage">
+ <a target="_blank" href="{{ plugin.info.homepage }}">({{ 'CorePluginsAdmin_PluginHomepage'|translate|replace({' ': '&nbsp;'})|raw }})</a>
+ </span>
+ {% endif %}
- {% if plugin.info.donate is defined and plugin.info.donate|length %}
- <div class="plugin-donation">
- {{ 'CorePluginsAdmin_LikeThisPlugin'|translate }} <a href="javascript:;" class="plugin-donation-link" data-overlay-id="overlay-{{ name|escape('html_attr') }}">{{ 'CorePluginsAdmin_ConsiderDonating'|translate }}</a>
- <div id="overlay-{{ name|escape('html_attr') }}" class="donation-overlay ui-confirm" title="{{ 'CorePluginsAdmin_LikeThisPlugin'|translate }}">
- <p>{{ 'CorePluginsAdmin_CommunityContributedPlugin'|translate }}</p>
- <p>{{ 'CorePluginsAdmin_ConsiderDonatingCreatorOf'|translate("<b>" ~ name ~ "</b>")|raw }}</p>
- <div class="donation-links">
- {% if plugin.info.donate.paypal is defined and plugin.info.donate.paypal %}
- <a class="donation-link paypal" target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&item_name=Piwik%20Plugin%20{{ name|escape('url') }}&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted&business={{ plugin.info.donate.paypal|escape('url') }}"><img src="plugins/CorePluginsAdmin/images/paypal_donate.jpg" height="30"/></a>
- {% endif %}
- {% if plugin.info.donate.flattr is defined and plugin.info.donate.flattr %}
- <a class="donation-link flattr" target="_blank" href="{{ plugin.info.donate.flattr }}"><img class="alignnone" title="Flattr" alt="" src="plugins/CorePluginsAdmin/images/flattr.png" height="29" /></a>
- {% endif %}
- {% if plugin.info.donate.bitcoin is defined and plugin.info.donate.bitcoin %}
- <div class="donation-link bitcoin">
- <span>Donate Bitcoins to:</span>
- <a href="bitcoin:{{ plugin.info.donate.bitcoin|escape('url') }}">{{ plugin.info.donate.bitcoin }}</a>
- </div>
- {% endif %}
+ {% if plugin.info.donate is defined and plugin.info.donate|length %}
+ <div class="plugin-donation">
+ {{ 'CorePluginsAdmin_LikeThisPlugin'|translate }} <a href="javascript:;" class="plugin-donation-link" data-overlay-id="overlay-{{ name|escape('html_attr') }}">{{ 'CorePluginsAdmin_ConsiderDonating'|translate }}</a>
+ <div id="overlay-{{ name|escape('html_attr') }}" class="donation-overlay ui-confirm" title="{{ 'CorePluginsAdmin_LikeThisPlugin'|translate }}">
+ <p>{{ 'CorePluginsAdmin_CommunityContributedPlugin'|translate }}</p>
+ <p>{{ 'CorePluginsAdmin_ConsiderDonatingCreatorOf'|translate("<b>" ~ name ~ "</b>")|raw }}</p>
+ <div class="donation-links">
+ {% if plugin.info.donate.paypal is defined and plugin.info.donate.paypal %}
+ <a class="donation-link paypal" target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&item_name=Piwik%20Plugin%20{{ name|escape('url') }}&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted&business={{ plugin.info.donate.paypal|escape('url') }}"><img src="plugins/CorePluginsAdmin/images/paypal_donate.jpg" height="30"/></a>
+ {% endif %}
+ {% if plugin.info.donate.flattr is defined and plugin.info.donate.flattr %}
+ <a class="donation-link flattr" target="_blank" href="{{ plugin.info.donate.flattr }}"><img class="alignnone" title="Flattr" alt="" src="plugins/CorePluginsAdmin/images/flattr.png" height="29" /></a>
+ {% endif %}
+ {% if plugin.info.donate.bitcoin is defined and plugin.info.donate.bitcoin %}
+ <div class="donation-link bitcoin">
+ <span>Donate Bitcoins to:</span>
+ <a href="bitcoin:{{ plugin.info.donate.bitcoin|escape('url') }}">{{ plugin.info.donate.bitcoin }}</a>
</div>
- <input role="no" type="button" value="{{ 'General_Close'|translate }}"/>
+ {% endif %}
</div>
+ <input role="no" type="button" value="{{ 'General_Close'|translate }}"/>
</div>
- {% endif %}
- </div>
- {% if plugin.info.license is defined %}
- <div class="plugin-license">
- {% if plugin.info.license_homepage is defined %}<a title="{{ 'CorePluginsAdmin_LicenseHomepage'|translate }}" rel="noreferrer" target="_blank" href="{{ plugin.info.license_homepage }}">{% endif %}{{ plugin.info.license }}{% if plugin.info.license_homepage is defined %}</a>{% endif %}
- </div>
+ </div>
{% endif %}
- {% if plugin.info.authors is defined %}
- <div class="plugin-author">
- <cite>By
- {% if plugin.info.authors is defined -%}
- {% spaceless %}
- {% for author in plugin.info.authors if author.name %}
- {% if author.homepage is defined %}
- <a title="{{ 'CorePluginsAdmin_AuthorHomepage'|translate }}" href="{{ author.homepage }}" rel="noreferrer" target="_blank">{{ author.name }}</a>
- {% else %}
- {{ author.name }}
+ </div>
+ {% if plugin.info.license is defined %}
+ <div class="plugin-license">
+ {% if plugin.info.license_homepage is defined %}<a title="{{ 'CorePluginsAdmin_LicenseHomepage'|translate }}" rel="noreferrer" target="_blank" href="{{ plugin.info.license_homepage }}">{% endif %}{{ plugin.info.license }}{% if plugin.info.license_homepage is defined %}</a>{% endif %}
+ </div>
+ {% endif %}
+ {% if plugin.info.authors is defined %}
+ <div class="plugin-author">
+ <cite>By
+ {% if plugin.info.authors is defined -%}
+ {% spaceless %}
+ {% for author in plugin.info.authors if author.name %}
+ {% if author.homepage is defined %}
+ <a title="{{ 'CorePluginsAdmin_AuthorHomepage'|translate }}" href="{{ author.homepage }}" rel="noreferrer" target="_blank">{{ author.name }}</a>
+ {% else %}
+ {{ author.name }}
+ {% endif %}
+ {% if loop.index < plugin.info.authors|length %}
+ ,
{% endif %}
- {% if loop.index < plugin.info.authors|length %}
- ,
- {% endif %}
- {% endfor %}
- {% endspaceless %}
- {%- endif %}.</cite>
- </div>
- {% endif %}
- </td>
- <td class="status" {% if isDefaultTheme %}style="border-left-width:0px;"{% endif %}>
- {% if not isDefaultTheme -%}
+ {% endfor %}
+ {% endspaceless %}
+ {%- endif %}.</cite>
+ </div>
+ {% endif %}
+ </td>
+ <td class="status" {% if isDefaultTheme %}style="border-left-width:0px;"{% endif %}>
+ {% if not isDefaultTheme -%}
- {% if plugin.activated %}
- {{ 'CorePluginsAdmin_Active'|translate }}
- {% else %}
- {{ 'CorePluginsAdmin_Inactive'|translate }}
- {% if plugin.uninstallable and displayAdminLinks %} <br/> - <a data-pluginName="{{ name|escape('html_attr') }}" class="uninstall" href='index.php?module=CorePluginsAdmin&action=uninstall&pluginName={{ name }}&nonce={{ uninstallNonce }}'>{{ 'CorePluginsAdmin_ActionUninstall'|translate }}</a>{% endif %}
- {% endif %}
+ {% if plugin.activated %}
+ {{ 'CorePluginsAdmin_Active'|translate }}
+ {% else %}
+ {{ 'CorePluginsAdmin_Inactive'|translate }}
+ {% if plugin.uninstallable and displayAdminLinks %} <br/> - <a data-plugin-name="{{ name|escape('html_attr') }}" class="uninstall" href='index.php?module=CorePluginsAdmin&action=uninstall&pluginName={{ name }}&nonce={{ uninstallNonce }}'>{{ 'CorePluginsAdmin_ActionUninstall'|translate }}</a>{% endif %}
+ {% endif %}
- {%- endif %}
- </td>
+ {%- endif %}
+ </td>
- {% if displayAdminLinks %}
- <td class="togl action-links" {% if isDefaultTheme %}style="border-left-width:0px;"{% endif %}>
- {% if not isDefaultTheme -%}
+ {% if displayAdminLinks %}
+ <td class="togl action-links" {% if isDefaultTheme %}style="border-left-width:0px;"{% endif %}>
+ {% if not isDefaultTheme -%}
- {% if plugin.invalid is defined or plugin.alwaysActivated %}
+ {% if plugin.invalid is defined or plugin.alwaysActivated %}
+ -
+ {% else %}
+ {% if plugin.activated %}
+ <a href='index.php?module=CorePluginsAdmin&action=deactivate&pluginName={{ name }}&nonce={{ deactivateNonce }}'>{{ 'CorePluginsAdmin_Deactivate'|translate }}</a>
+ {% elseif plugin.missingRequirements %}
-
{% else %}
- {% if plugin.activated %}
- <a href='index.php?module=CorePluginsAdmin&action=deactivate&pluginName={{ name }}&nonce={{ deactivateNonce }}'>{{ 'CorePluginsAdmin_Deactivate'|translate }}</a>
- {% elseif plugin.missingRequirements %}
- -
- {% else %}
- <a href='index.php?module=CorePluginsAdmin&action=activate&pluginName={{ name }}&nonce={{ activateNonce }}'>{{ 'CorePluginsAdmin_Activate'|translate }}</a>
- {% endif %}
+ <a href='index.php?module=CorePluginsAdmin&action=activate&pluginName={{ name }}&nonce={{ activateNonce }}'>{{ 'CorePluginsAdmin_Activate'|translate }}</a>
{% endif %}
-
- {%- endif %}
- </td>
{% endif %}
- </tr>
- {% endif %}
- {% endfor %}
- </tbody>
- </table>
+
+ {%- endif %}
+ </td>
+ {% endif %}
+ </tr>
+ {% endif %}
+ {% endfor %}
+ </tbody>
+</table>
+
+<div class="tableActionBar">
+ {% if isTheme %}
+ <a href="{{ linkTo({'action':'browseThemes', 'sort': ''}) }}"><span class="icon-add"></span> {{ 'CorePluginsAdmin_InstallNewThemes'|translate }}</a>
+ {% else %}
+ <a href="{{ linkTo({'action':'browsePlugins', 'sort': ''}) }}"><span class="icon-add"></span> {{ 'CorePluginsAdmin_InstallNewPlugins'|translate }}</a>
+ {% endif %}
</div>
{% endmacro %}
diff --git a/plugins/CorePluginsAdmin/templates/marketplace.twig b/plugins/CorePluginsAdmin/templates/marketplace.twig
index 439d49b005..61f46f8cd2 100644
--- a/plugins/CorePluginsAdmin/templates/marketplace.twig
+++ b/plugins/CorePluginsAdmin/templates/marketplace.twig
@@ -5,23 +5,11 @@
{% block content %}
- <div class="marketplace">
-
- <h2 piwik-enriched-headline feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}">
- {{ title }}
- </h2>
-
- <ul class="nav nav-pills">
- <li {% if not showThemes %}class="active"{% endif %}>
- <a href="{{ linkTo({'show': 'plugins'}) }}">{{ 'General_Plugins'|translate }}</a>
- </li>
- <li {% if showThemes %}class="active"{% endif %}>
- <a href="{{ linkTo({'show': 'themes'}) }}">{{ 'CorePluginsAdmin_Themes'|translate }}</a>
- </li>
- </ul>
-
- <div class="marketplace-max-width">
+ <div class="marketplace" piwik-marketplace>
+ <div piwik-content-intro>
+ <h2 piwik-enriched-headline feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}"
+ >{{ title|e('html_attr') }}</h2>
<p>
{% if showThemes %}
{{ 'CorePluginsAdmin_ThemesDescription'|translate }}
@@ -46,21 +34,55 @@
</p>
{% endif %}
- </div>
-
- <hr/>
-
- <div class="ui-confirm" id="installPluginByUpload">
- <h2>{{ 'CorePluginsAdmin_TeaserExtendPiwikByUpload'|translate }}</h2>
- <p class="description"> {{ 'CorePluginsAdmin_AllowedUploadFormats'|translate }} </p>
-
- <form enctype="multipart/form-data" method="post" id="uploadPluginForm"
- action="{{ linkTo({'action':'uploadPlugin', 'nonce': installNonce}) }}">
- <input type="file" name="pluginZip">
- <br />
- <input class="startUpload" type="submit" value="{{ 'CorePluginsAdmin_UploadZipFile'|translate }}">
- </form>
+ <div class="ui-confirm" id="installPluginByUpload">
+ <h2>{{ 'CorePluginsAdmin_TeaserExtendPiwikByUpload'|translate }}</h2>
+
+ <p class="description"> {{ 'CorePluginsAdmin_AllowedUploadFormats'|translate }} </p>
+
+ <form enctype="multipart/form-data" method="post" id="uploadPluginForm"
+ action="{{ linkTo({'action':'uploadPlugin', 'nonce': installNonce}) }}">
+ <input type="file" name="pluginZip">
+ <br />
+ <input class="startUpload btn" type="submit" value="{{ 'CorePluginsAdmin_UploadZipFile'|translate }}">
+ </form>
+ </div>
+
+ <div class="row marketplaceActions" ng-controller="PiwikMarketplaceController as marketplace">
+ <div piwik-field uicontrol="select" name="plugin_type"
+ class="col s12 m4"
+ ng-model="marketplace.pluginType"
+ ng-change="marketplace.changePluginType()"
+ title="{{ 'Show'|translate|e('html_attr') }}"
+ value="{{ pluginType }}"
+ full-width="true"
+ options="{{ pluginTypeOptions|json_encode }}">
+ </div>
+
+ <div piwik-field uicontrol="select" name="plugin_sort"
+ title="{{ 'Sort'|translate|e('html_attr') }}"
+ value="{{ sort }}"
+ ng-model="marketplace.pluginSort"
+ ng-change="marketplace.changePluginSort()"
+ class="col s12 m4"
+ full-width="true"
+ options="{{ pluginSortOptions|json_encode }}">
+ </div>
+
+ {# Hide filters and search for themes because we don't have many of them #}
+ {% if not showThemes %}
+ <div class="col s12 m4 ">
+ <form action="{{ linkTo({'sort': ''}) }}" method="post" class="plugin-search">
+ <div piwik-field uicontrol="text" name="query"
+ title="{{ 'General_Search'|translate }} {{ plugins|length }} {{ 'General_Plugins'|translate|lcfirst }}..."
+ value="{{ query }}"
+ full-width="true">
+ </div>
+ <span class="icon-search" onclick="$('form.plugin-search').submit();"></span>
+ </form>
+ </div>
+ {% endif %}
+ </div>
</div>
{% include '@CorePluginsAdmin/marketplace/plugin-list.twig' %}
diff --git a/plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig b/plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig
index eaf166e466..8fe294eadb 100644
--- a/plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig
+++ b/plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig
@@ -1,56 +1,24 @@
-<div class="row marketplace-max-width">
-
- {# Hide filters and search for themes because we don't have many of them #}
- {% if not showThemes %}
- <div class="col-sm-12 clearfix">
-
- <form action="{{ linkTo({'sort': ''}) }}" method="post" class="plugin-search">
- <input value="{{ query }}" placeholder="{{ 'General_Search'|translate }} {{ plugins|length }} {{ 'General_Plugins'|translate|lcfirst }}..." type="text" name="query"/>
- <button type="submit">{{ 'General_Search'|translate }}</button>
- </form>
-
- <ul class="nav nav-pills">
- <li {% if 'popular' == sort %}class="active"{% endif %}>
- <a href="{{ linkTo({'sort': 'popular', 'query': ''}) }}">
- {{ 'CorePluginsAdmin_SortByPopular'|translate }}
- </a>
- </li>
- <li {% if 'newest' == sort %}class="active"{% endif %}>
- <a href="{{ linkTo({'sort': 'newest', 'query': ''}) }}">
- {{ 'CorePluginsAdmin_SortByNewest'|translate }}
- </a>
- </li>
- <li {% if 'alpha' == sort %}class="active"{% endif %}>
- <a href="{{ linkTo({'sort': 'alpha', 'query': ''}) }}">
- {{ 'CorePluginsAdmin_SortByAlpha'|translate }}
- </a>
- </li>
- </ul>
-
- </div>
- {% endif %}
-
+{% if plugins|length > 0 %}
+ <div class="row">
{% for plugin in plugins %}
-
- <div class="col-md-4">
- <div class="panel plugin">
-
- <div class="panel-heading">
- <h3 class="panel-title panel-title-block" title="{{ 'General_MoreDetails'|translate }}">
- <a class="plugin-details" href="#" data-pluginName="{{ plugin.name }}">{{ plugin.name }}</a>
+ {% if plugin.isDownloadable %}
+ <div class="col s12 m6 l4">
+ {% embed 'contentBlock.twig' with {'title': ''} %}
+ {% block content %}
+ <div class="plugin">
+ <h3 class="card-title" title="{{ 'General_MoreDetails'|translate }}">
+ <a href="#" piwik-plugin-name="{{ plugin.name }}">{{ plugin.name }}</a>
</h3>
- </div>
- <div class="panel-body">
<p class="description">
{{ plugin.description }}
- <a class="more plugin-details" href="#" data-pluginName="{{ plugin.name }}" title="{{ 'General_MoreDetails'|translate }}">
+ <a class="more" href="#" piwik-plugin-name="{{ plugin.name }}" title="{{ 'General_MoreDetails'|translate }}">
&rsaquo; {{ 'General_MoreLowerCase'|translate }}</a>
</p>
{% if showThemes %}
{# Screenshot for themes #}
- <a class="more plugin-details" href="#" data-pluginName="{{ plugin.name }}">
+ <a class="more" href="#" piwik-plugin-name="{{ plugin.name }}">
<img title="{{ 'General_MoreDetails'|translate }}"
class="preview" src="{{ plugin.screenshots|first }}?w=250&h=150"/></a>
{% endif %}
@@ -59,7 +27,7 @@
<li>
{{ 'CorePluginsAdmin_Version'|translate }}: {{ plugin.latestVersion }}
{% if plugin.canBeUpdated %}
- <a class="plugin-details update-available" href="#" data-pluginName="{{ plugin.name }}" data-activePluginTab="changelog"
+ <a class="update-available" href="#" piwik-plugin-name="{{ plugin.name }}" data-activePluginTab="changelog"
title="{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}">
{{ 'CorePluginsAdmin_NewVersion'|translate }}</a>
{% endif %}
@@ -68,43 +36,47 @@
<li>{{ 'General_Downloads'|translate }}: {{ plugin.numDownloads }}</li>
<li>{{ 'CorePluginsAdmin_Developer'|translate }}: {{ pluginsMacro.pluginDeveloper(plugin.owner) }}</li>
</ul>
- </div>
- {% if isSuperUser %}
- <div class="panel-footer" data-pluginName="{{ plugin.name }}">
- {% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
- <a class="btn btn-block"
- href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}">
- {{ 'CoreUpdater_UpdateTitle'|translate }}
- </a>
- {% elseif plugin.isInstalled %}
- <button class="btn btn-noop btn-block">
- {{ 'General_Installed'|translate }}
- </button>
- {% elseif plugin.missingRequirements|length > 0 %}
- <a class="btn btn-link btn-block plugin-details" href="#" data-pluginName="{{ plugin.name }}" title="{{ 'General_MoreDetails'|translate }}">
- {{ 'CorePluginsAdmin_CannotInstall'|translate }}
- </a>
- {% else %}
- <a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
- class="btn btn-block">
- {{ 'CorePluginsAdmin_ActionInstall'|translate }}
- </a>
- {% endif %}
- </div>
- {% endif %}
+ {% if isSuperUser %}
+ <div class="footer" piwik-plugin-name="{{ plugin.name }}">
+ {% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
+ <a class="btn"
+ href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}">
+ {{ 'CoreUpdater_UpdateTitle'|translate }}
+ </a>
+ {% elseif plugin.isInstalled %}
+ <button class="btn btn-noop btn-block">
+ {{ 'General_Installed'|translate }}
+ </button>
+ {% elseif plugin.missingRequirements|length > 0 %}
+ <a class="btn btn-link btn-block" href="#" piwik-plugin-name="{{ plugin.name }}" title="{{ 'General_MoreDetails'|translate }}">
+ {{ 'CorePluginsAdmin_CannotInstall'|translate }}
+ </a>
+ {% else %}
+ <a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
+ class="btn">
+ {{ 'CorePluginsAdmin_ActionInstall'|translate }}
+ </a>
+ {% endif %}
+ </div>
+ {% endif %}
- </div>
+ </div>
+ {% endblock %}
+ {% endembed %}
</div>
+ {% endif %}
+{% endfor %}
+ </div>
+{% endif %}
- {% endfor %}
-
- {% if plugins|length == 0 %}
+{% if plugins|length == 0 %}
+ <div piwik-content-block>
{% if showThemes %}
{{ 'CorePluginsAdmin_NoThemesFound'|translate }}
{% else %}
{{ 'CorePluginsAdmin_NoPluginsFound'|translate }}
{% endif %}
- {% endif %}
+ </div>
+{% endif %}
-</div>
diff --git a/plugins/CorePluginsAdmin/templates/pluginDetails.twig b/plugins/CorePluginsAdmin/templates/pluginDetails.twig
index 7359ce3a19..33a73f3c80 100644
--- a/plugins/CorePluginsAdmin/templates/pluginDetails.twig
+++ b/plugins/CorePluginsAdmin/templates/pluginDetails.twig
@@ -5,12 +5,12 @@
<div class="pluginDetails">
{% if errorMessage %}
{{ errorMessage }}
- {% elseif plugin %}
+ {% elseif plugin and plugin.isDownloadable %}
{% set latestVersion = plugin.versions[plugin.versions|length - 1] %}
<div class="header">
- <div class="intro" style="width:75%;float:left;">
+ <div class="intro">
<h2>{{ plugin.name }}</h2>
<p class="description">
{% if plugin.featured %}
@@ -19,8 +19,7 @@
{{ plugin.description }}
</p>
</div>
- <div class="width:25%;float:left;">
-
+ <div class="actionButton">
{% if isSuperUser %}
{% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
<a class="install update"
@@ -37,35 +36,40 @@
</div>
<div class="content">
- <div style="width:75%;float:left;">
+ <div class="contentDetails">
+ <div id="pluginDetailsTabs" class="row">
+ <div class="col s12">
+ <ul class="tabs">
+ <li class="tab col s3"><a href="#tabs-description">{{ 'General_Description'|translate }}</a></li>
+
+ {% if latestVersion.readmeHtml.faq %}
+ <li class="tab col s3"><a href="#tabs-faq">{{ 'General_Faq'|translate }}</a></li>
+ {% endif %}
- <div id="pluginDetailsTabs">
- <ul>
- <li><a href="#tabs-description">{{ 'General_Description'|translate }}</a></li>
- {% if latestVersion.readmeHtml.faq %}
- <li><a href="#tabs-faq">{{ 'General_Faq'|translate }}</a></li>
- {% endif %}
- <li><a href="#tabs-changelog">{{ 'CorePluginsAdmin_Changelog'|translate }}</a></li>
- {% if plugin.screenshots|length %}
- <li><a href="#tabs-screenshots">{{ 'CorePluginsAdmin_Screenshots'|translate }}</a></li>
- {% endif %}
- {% if latestVersion.readmeHtml.support %}
- <li><a href="#tabs-support">{{ 'CorePluginsAdmin_Support'|translate }}</a></li>
- {% endif %}
- </ul>
+ <li class="tab col s3"><a href="#tabs-changelog">{{ 'CorePluginsAdmin_Changelog'|translate }}</a></li>
+
+ {% if plugin.screenshots|length %}
+ <li class="tab col s3"><a href="#tabs-screenshots">{{ 'CorePluginsAdmin_Screenshots'|translate }}</a></li>
+ {% endif %}
+
+ {% if latestVersion.readmeHtml.support is defined and latestVersion.readmeHtml.support %}
+ <li class="tab col s3"><a href="#tabs-support">{{ 'CorePluginsAdmin_Support'|translate }}</a></li>
+ {% endif %}
+ </ul>
+ </div>
- <div id="tabs-description">
+ <div id="tabs-description" class="tab-content col s12">
{{ pluginsMacro.missingRequirementsPleaseUpdateNotice(plugin) }}
{{ latestVersion.readmeHtml.description|raw }}
</div>
{% if latestVersion.readmeHtml.faq %}
- <div id="tabs-faq">
+ <div id="tabs-faq" class="tab-content col s12">
{{ latestVersion.readmeHtml.faq|raw }}
</div>
{% endif %}
- <div id="tabs-changelog">
+ <div id="tabs-changelog" class="tab-content col s12">
{{ pluginsMacro.missingRequirementsPleaseUpdateNotice(plugin) }}
{% if plugin.canBeUpdated %}
<div class="alert alert-warning">
@@ -99,7 +103,7 @@
</div>
{% if plugin.screenshots|length %}
- <div id="tabs-screenshots">
+ <div id="tabs-screenshots" class="tab-content col s12">
<div class="thumbnails">
{% for screenshot in plugin.screenshots %}
<div class="thumbnail">
@@ -113,8 +117,8 @@
</div>
{% endif %}
- {% if latestVersion.readmeHtml.support %}
- <div id="tabs-support">
+ {% if latestVersion.readmeHtml.support is defined and latestVersion.readmeHtml.support %}
+ <div id="tabs-support" class="tab-content col s12">
{{ latestVersion.readmeHtml.support|raw }}
@@ -123,8 +127,7 @@
</div>
</div>
- <div class="metadata" style="width:25%;float:left;">
- <p><br /></p>
+ <div class="metadata">
<dl>
<dt>{{ 'CorePluginsAdmin_Version'|translate }}</dt>
<dd>{{ plugin.latestVersion }}</dd>
@@ -188,7 +191,8 @@
}
{% endif %}
- $( "#pluginDetailsTabs" ).tabs({active: active >= 0 ? active : 0});
+ $('#pluginDetailsTabs .tabs').tabs();
+ $('#pluginDetailsTabs .tabs').tabs('select_tab', active >= 0 ? active : 0);
$('.pluginDetails a').each(function (index, a) {
var link = $(a).attr('href');
diff --git a/plugins/CorePluginsAdmin/templates/plugins.twig b/plugins/CorePluginsAdmin/templates/plugins.twig
index 10910bda57..4a8649710b 100644
--- a/plugins/CorePluginsAdmin/templates/plugins.twig
+++ b/plugins/CorePluginsAdmin/templates/plugins.twig
@@ -5,32 +5,43 @@
{% set title %}{{ 'CorePluginsAdmin_PluginsManagement'|translate }}{% endset %}
{% block content %}
-<div class="pluginsManagement">
- {% if pluginsHavingUpdate|length %}
- <h2>{{ pluginsHavingUpdate|length }} Update(s) available</h2>
-
- <p>{{ 'CorePluginsAdmin_InfoPluginUpdateIsRecommended'|translate }}</p>
-
- {{ plugins.tablePluginUpdates(pluginsHavingUpdate, updateNonce, activateNonce, 0) }}
- {% endif %}
-
- <h2 piwik-enriched-headline>{{ title }}</h2>
+<div piwik-content-intro>
+ <h2 piwik-enriched-headline>
+ {{ title|e('html_attr') }}
+ </h2>
<p>{{ 'CorePluginsAdmin_PluginsExtendPiwik'|translate }}
{{ 'CorePluginsAdmin_OncePluginIsInstalledYouMayActivateHere'|translate }}
- {% if not isPluginsAdminEnabled %}
- <br/>{{ 'CorePluginsAdmin_DoMoreContactPiwikAdmins'|translate }}
- {% endif %}
+ {% if isMarketplaceEnabled %}
+ {{ 'CorePluginsAdmin_TeaserExtendPiwikByPlugin'|translate('<a href="' ~ linkTo({'action':'browsePlugins', 'sort': ''}) ~ '">', '</a>')|raw }}
+ {% endif %}
+
+ {% if not isPluginsAdminEnabled %}
+ <br/>{{ 'CorePluginsAdmin_DoMoreContactPiwikAdmins'|translate }}
+ {% endif %}
<br />
{{ 'CorePluginsAdmin_ChangeLookByManageThemes'|translate('<a href="' ~ linkTo({'action': 'themes'}) ~'">', '</a>')|raw }}
</p>
+</div>
- {{ plugins.pluginsFilter(false, isMarketplaceEnabled) }}
+{% if pluginsHavingUpdate|length %}
+ <div piwik-content-block content-title="{{ pluginsHavingUpdate|length }} Update(s) available">
+
+ <p>{{ 'CorePluginsAdmin_InfoPluginUpdateIsRecommended'|translate }}</p>
+
+ {{ plugins.tablePluginUpdates(pluginsHavingUpdate, updateNonce, activateNonce, 0) }}
+ </div>
+{% endif %}
+
+<div piwik-content-block content-title="{{ 'CorePluginsAdmin_InstalledPlugins'|translate|e('html_attr') }}" piwik-plugin-management>
+
+ {{ plugins.pluginsFilter() }}
{{ plugins.tablePlugins(pluginsInfo, pluginNamesHavingSettings, activateNonce, deactivateNonce, uninstallNonce, false, marketplacePluginNames, isPluginsAdminEnabled) }}
</div>
+
{% endblock %} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/templates/themes.twig b/plugins/CorePluginsAdmin/templates/themes.twig
index 4f226956b7..d898c3c799 100644
--- a/plugins/CorePluginsAdmin/templates/themes.twig
+++ b/plugins/CorePluginsAdmin/templates/themes.twig
@@ -5,23 +5,37 @@
{% set title %}{{ 'CorePluginsAdmin_ThemesManagement'|translate }}{% endset %}
{% block content %}
-<div style="max-width:980px;">
- <h2 piwik-enriched-headline>{{ title }}</h2>
+<div piwik-content-intro>
+ <h2 piwik-enriched-headline>
+ {{ title|e('html_attr') }}
+ </h2>
- <p>{{ 'CorePluginsAdmin_ThemesDescription'|translate }}
- {% if otherUsersCount > 0 %}
- <br/> {{ 'CorePluginsAdmin_InfoThemeIsUsedByOtherUsersAsWell'|translate(otherUsersCount, themeEnabled) }}
- {% endif %}
- {% if not isPluginsAdminEnabled %}
- <br/>{{ 'CorePluginsAdmin_DoMoreContactPiwikAdmins'|translate }}
- {% endif %}
+ <p>
+ {{ 'CorePluginsAdmin_ThemesDescription'|translate }}
+
+ {% if isMarketplaceEnabled %}
+ {{ 'CorePluginsAdmin_TeaserExtendPiwikByTheme'|translate('<a href="' ~ linkTo({'action':'browseThemes', 'sort': ''}) ~ '">', '</a>')|raw }}
+ {% endif %}
+
+ {% if otherUsersCount > 0 %}
+ <br/> {{ 'CorePluginsAdmin_InfoThemeIsUsedByOtherUsersAsWell'|translate(otherUsersCount, themeEnabled) }}
+ {% endif %}
+ {% if not isPluginsAdminEnabled %}
+ <br/>{{ 'CorePluginsAdmin_DoMoreContactPiwikAdmins'|translate }}
+ {% endif %}
</p>
+</div>
- {{ plugins.pluginsFilter(true, isMarketplaceEnabled) }}
+<div piwik-content-block content-title="{{ 'CorePluginsAdmin_InstalledThemes'|translate|e('html_attr') }}"
+ piwik-plugin-management>
- {{ plugins.tablePlugins(pluginsInfo, pluginNamesHavingSettings, activateNonce, deactivateNonce, uninstallNonce, true, marketplacePluginNames, isPluginsAdminEnabled ) }}
+ <p>
+ </p>
+
+ {{ plugins.pluginsFilter() }}
+ {{ plugins.tablePlugins(pluginsInfo, pluginNamesHavingSettings, activateNonce, deactivateNonce, uninstallNonce, true, marketplacePluginNames, isPluginsAdminEnabled ) }}
</div>
{% endblock %}
diff --git a/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php b/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php
index bbfd7af081..06681f404d 100644
--- a/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php
+++ b/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php
@@ -9,8 +9,10 @@
namespace Piwik\Plugins\CorePluginsAdmin\tests\Integration;
use Piwik\Config;
+use Piwik\Container\StaticContainer;
use Piwik\Option;
use Piwik\Plugins\CorePluginsAdmin\UpdateCommunication;
+use Piwik\Plugins\CoreUpdater\SystemSettings;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
@@ -26,37 +28,30 @@ class UpdateCommunicationTest extends IntegrationTestCase
*/
private $updateCommunication;
+ /**
+ * @var SystemSettings
+ */
+ private $settings;
+
public function setUp()
{
parent::setUp();
- $this->updateCommunication = new UpdateCommunication();
- $this->updateCommunication->enable();
+ $this->settings = StaticContainer::get('Piwik\Plugins\CoreUpdater\SystemSettings');
+ $this->settings->sendPluginUpdateEmail->setValue(true);
+
+ $this->updateCommunication = new UpdateCommunication($this->settings);
}
public function test_canBeEnabled()
{
- $this->assertTrue($this->updateCommunication->canBeEnabled());
+ $this->assertTrue(UpdateCommunication::canBeEnabled());
Config::getInstance()->General['enable_update_communication'] = 0;
- $this->assertFalse($this->updateCommunication->canBeEnabled());
+ $this->assertFalse(UpdateCommunication::canBeEnabled());
Config::getInstance()->General['enable_update_communication'] = 1;
- $this->assertTrue($this->updateCommunication->canBeEnabled());
- }
-
- public function test_enable()
- {
- $this->updateCommunication->enable();
- $this->assertTrue($this->updateCommunication->isEnabled());
- }
-
- public function test_disable()
- {
- $this->assertTrue($this->updateCommunication->isEnabled());
-
- $this->updateCommunication->disable();
- $this->assertFalse($this->updateCommunication->isEnabled());
+ $this->assertTrue(UpdateCommunication::canBeEnabled());
}
public function test_isEnabled_shouldReturnFalse_IfCannotBeEnabled()
@@ -160,7 +155,10 @@ Installation_HappyAnalysing";
*/
private function getCommunicationMock($pluginsHavingUpdate)
{
- $mock = $this->getMock('\Piwik\Plugins\CorePluginsAdmin\UpdateCommunication', array('getPluginsHavingUpdate', 'sendEmailNotification'));
+ $mock = $this->getMockBuilder('\Piwik\Plugins\CorePluginsAdmin\UpdateCommunication')
+ ->setMethods(array('getPluginsHavingUpdate', 'sendEmailNotification'))
+ ->setConstructorArgs(array($this->settings))
+ ->getMock();
$mock->expects($this->any())
->method('getPluginsHavingUpdate')
diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php
index df52b296aa..4e3f20ff42 100644
--- a/plugins/CoreUpdater/Controller.php
+++ b/plugins/CoreUpdater/Controller.php
@@ -9,6 +9,7 @@
namespace Piwik\Plugins\CoreUpdater;
use Exception;
+use Piwik\AssetManager;
use Piwik\Common;
use Piwik\Config;
use Piwik\DbHelper;
@@ -46,6 +47,66 @@ class Controller extends \Piwik\Plugin\Controller
parent::__construct();
}
+ /**
+ * Return the base.less compiled to css
+ *
+ * @return string
+ */
+ public function getUpdaterCss()
+ {
+ Common::sendHeader('Content-Type: text/css');
+ Common::sendHeader('Cache-Control: max-age=' . (60 * 60));
+
+ $files = array(
+ 'plugins/Morpheus/stylesheets/base/bootstrap.css',
+ 'plugins/Morpheus/stylesheets/base/icons.css',
+ 'libs/jquery/themes/base/jquery-ui.min.css',
+ 'libs/bower_components/materialize/dist/css/materialize.min.css',
+ 'plugins/Morpheus/stylesheets/base.less',
+ 'plugins/Morpheus/stylesheets/general/_forms.less',
+ 'plugins/Morpheus/stylesheets/simple_structure.css',
+ 'plugins/CoreHome/stylesheets/jquery.ui.autocomplete.css',
+ 'plugins/CoreUpdater/stylesheets/updateLayout.css'
+ );
+
+ return AssetManager::compileCustomStylesheets($files);
+ }
+
+ /**
+ * Return the base.less compiled to css
+ *
+ * @return string
+ */
+ public function getUpdaterJs()
+ {
+ Common::sendHeader('Content-Type: application/javascript; charset=UTF-8');
+ Common::sendHeader('Cache-Control: max-age=' . (60 * 60));
+
+ $files = array(
+ 'libs/bower_components/jquery/dist/jquery.min.js',
+ 'libs/bower_components/jquery-ui/ui/minified/jquery-ui.min.js',
+ 'libs/bower_components/materialize/dist/js/materialize.min.js',
+ 'plugins/Morpheus/javascripts/piwikHelper.js',
+ 'plugins/CoreHome/javascripts/donate.js',
+ 'plugins/CoreUpdater/javascripts/updateLayout.js',
+ 'libs/bower_components/angular/angular.min.js',
+ 'libs/bower_components/angular-sanitize/angular-sanitize.js',
+ 'libs/bower_components/angular-animate/angular-animate.js',
+ 'libs/bower_components/angular-cookies/angular-cookies.js',
+ 'libs/bower_components/ngDialog/js/ngDialog.min.js',
+ 'plugins/CoreHome/angularjs/common/services/service.module.js',
+ 'plugins/CoreHome/angularjs/common/filters/filter.module.js',
+ 'plugins/CoreHome/angularjs/common/filters/translate.js',
+ 'plugins/CoreHome/angularjs/common/directives/directive.module.js',
+ 'plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js',
+ 'plugins/CoreHome/angularjs/piwikApp.config.js',
+ 'plugins/CoreHome/angularjs/piwikApp.js',
+ 'plugins/Installation/javascripts/installation.js',
+ );
+
+ return AssetManager::compileCustomJs($files);
+ }
+
public function newVersionAvailable()
{
Piwik::checkUserHasSuperUserAccess();
diff --git a/plugins/CoreUpdater/CoreUpdater.php b/plugins/CoreUpdater/CoreUpdater.php
index 77298402fe..650569a922 100644
--- a/plugins/CoreUpdater/CoreUpdater.php
+++ b/plugins/CoreUpdater/CoreUpdater.php
@@ -55,20 +55,23 @@ class CoreUpdater extends \Piwik\Plugin
$module = Common::getRequestVar('module', '', 'string');
$action = Common::getRequestVar('action', '', 'string');
+ if ($module == 'CoreUpdater'
+ // Proxy module is used to redirect users to piwik.org, should still work when Piwik must be updated
+ || $module == 'Proxy'
+ // Do not show update page during installation.
+ || $module == 'Installation'
+ || ($module == 'LanguagesManager' && $action == 'saveLanguage')) {
+ return;
+ }
+
$updater = new PiwikCoreUpdater();
$updates = $updater->getComponentsWithNewVersion(array('core' => Version::VERSION));
+
if (!empty($updates)) {
Filesystem::deleteAllCacheOnUpdate();
}
- if ($updater->getComponentUpdates() !== null
- && $module != 'CoreUpdater'
- // Proxy module is used to redirect users to piwik.org, should still work when Piwik must be updated
- && $module != 'Proxy'
- // Do not show update page during installation.
- && $module != 'Installation'
- && !($module == 'LanguagesManager'
- && $action == 'saveLanguage')
- ) {
+
+ if ($updater->getComponentUpdates() !== null) {
if (FrontController::shouldRethrowException()) {
throw new Exception("Piwik and/or some plugins have been upgraded to a new version. \n" .
"--> Please run the update process first. See documentation: http://piwik.org/docs/update/ \n");
diff --git a/plugins/CoreUpdater/SystemSettings.php b/plugins/CoreUpdater/SystemSettings.php
new file mode 100644
index 0000000000..4827a74676
--- /dev/null
+++ b/plugins/CoreUpdater/SystemSettings.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\CoreUpdater;
+
+use Piwik\Piwik;
+use Piwik\Plugin\ReleaseChannels;
+use Piwik\Plugins\CoreAdminHome\Controller as CoreAdminController;
+use Piwik\Plugins\CorePluginsAdmin\UpdateCommunication as PluginUpdateCommunication;
+use Piwik\Settings\Setting;
+use Piwik\Settings\FieldConfig;
+
+/**
+ * Defines Settings for CoreUpdater.
+ *
+ * Usage like this:
+ * $settings = new SystemSettings();
+ * $settings->metric->getValue();
+ * $settings->description->getValue();
+ */
+class SystemSettings extends \Piwik\Settings\Plugin\SystemSettings
+{
+ /** @var Setting */
+ public $releaseChannel;
+
+ /** @var Setting */
+ public $sendPluginUpdateEmail;
+
+ /**
+ * @var ReleaseChannels
+ */
+ private $releaseChannels;
+
+ public function __construct(ReleaseChannels $releaseChannels)
+ {
+ $this->releaseChannels = $releaseChannels;
+
+ parent::__construct();
+ }
+
+ protected function init()
+ {
+ $this->title = Piwik::translate('CoreAdminHome_UpdateSettings');
+
+ $isWritable = Piwik::hasUserSuperUserAccess() && CoreAdminController::isGeneralSettingsAdminEnabled();
+ $this->releaseChannel = $this->createReleaseChannel();
+ $this->releaseChannel->setIsWritableByCurrentUser($isWritable);
+
+ $isWritable = $isWritable && PluginUpdateCommunication::canBeEnabled();
+ $this->sendPluginUpdateEmail = $this->createSendPluginUpdateEmail();
+ $this->sendPluginUpdateEmail->setIsWritableByCurrentUser($isWritable);
+ }
+
+ private function createReleaseChannel()
+ {
+ $releaseChannels = $this->releaseChannels;
+ $default = 'latest_stable';
+
+ return $this->makeSettingManagedInConfigOnly('General', 'release_channel', $default, FieldConfig::TYPE_STRING, function (FieldConfig $field) use ($releaseChannels) {
+
+ $field->introduction = Piwik::translate('CoreAdminHome_ReleaseChannel');
+ $field->uiControl = FieldConfig::UI_CONTROL_RADIO;
+
+ $field->availableValues = array();
+ foreach ($releaseChannels->getAllReleaseChannels() as $channel) {
+ $name = $channel->getName();
+ $description = $channel->getDescription();
+ if (!empty($description)) {
+ $name .= ' (' . $description . ')';
+ }
+
+ $field->availableValues[$channel->getId()] = $name;
+ }
+
+ $field->validate = function ($channel) use ($releaseChannels) {
+ if (!$releaseChannels->isValidReleaseChannelId($channel)) {
+ throw new \Exception('Release channel is not valid');
+ };
+ };
+
+ $field->inlineHelp = Piwik::translate('CoreAdminHome_DevelopmentProcess',
+ array("<a href='?module=Proxy&action=redirect&url=http://piwik.org/participate/development-process/' target='_blank'>",
+ "</a>"))
+ . Piwik::translate('<br/>')
+ . Piwik::translate('CoreAdminHome_StableReleases',
+ array("<a href='?module=Proxy&action=redirect&url=http%3A%2F%2Fdeveloper.piwik.org%2Fguides%2Fcore-team-workflow%23influencing-piwik-development' target='_blank'>",
+ "</a>"))
+ . Piwik::translate('<br />')
+ . Piwik::translate('CoreAdminHome_LtsReleases');
+ });
+ }
+
+ private function createSendPluginUpdateEmail()
+ {
+ return $this->makeSetting('enable_plugin_update_communication', false, FieldConfig::TYPE_BOOL, function (FieldConfig $field) {
+ $field->introduction = Piwik::translate('CoreAdminHome_SendPluginUpdateCommunication');
+ $field->uiControl = FieldConfig::UI_CONTROL_RADIO;
+ $field->availableValues = array('1' => Piwik::translate('General_Yes'),
+ '0' => sprintf('%s (%s)', Piwik::translate('General_No'), Piwik::translate('General_Default')));
+ $field->inlineHelp = Piwik::translate('CoreAdminHome_SendPluginUpdateCommunicationHelp');
+ });
+ }
+
+}
diff --git a/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php
index d43e11ebf4..5b4a03cb37 100644
--- a/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php
+++ b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php
@@ -141,6 +141,8 @@ http://piwik.org/contact/";
*/
private function getCommunicationMock($methodsToOverwrite)
{
- return $this->getMock('\Piwik\Plugins\CoreUpdater\UpdateCommunication', $methodsToOverwrite);
+ return $this->getMockBuilder('\Piwik\Plugins\CoreUpdater\UpdateCommunication')
+ ->setMethods($methodsToOverwrite)
+ ->getMock();
}
}
diff --git a/plugins/CoreUpdater/stylesheets/updateLayout.css b/plugins/CoreUpdater/stylesheets/updateLayout.css
index 3c436bc5d3..18a6bd519b 100644
--- a/plugins/CoreUpdater/stylesheets/updateLayout.css
+++ b/plugins/CoreUpdater/stylesheets/updateLayout.css
@@ -23,10 +23,23 @@ li {
margin-left: 30px;
}
-#oneclickupdate .submit {
+#oneclickupdate .btn {
clear:none;
float:left;
}
form#oneclickupdate {
min-height: 50px;
+}
+
+
+body#simple .languageSelection {
+ color: #4183C4;
+ padding-top: 8px;
+}
+
+p strong {
+ font-weight: 700 !important;
+}
+strong {
+ font-weight: 700 !important;
} \ No newline at end of file
diff --git a/plugins/CoreUpdater/templates/layout.twig b/plugins/CoreUpdater/templates/layout.twig
index bb9f33c12b..ff447676b7 100644
--- a/plugins/CoreUpdater/templates/layout.twig
+++ b/plugins/CoreUpdater/templates/layout.twig
@@ -7,37 +7,14 @@
<meta name="viewport" content="initial-scale=1.0" />
<meta name="robots" content="noindex,nofollow">
- <link rel="stylesheet" type="text/css" href="libs/jquery/themes/base/jquery-ui.min.css"/>
- <link rel="stylesheet" type="text/css" href="index.php?module=Installation&action=getBaseCss"/>
- <link rel="stylesheet" type="text/css" href="plugins/Morpheus/stylesheets/simple_structure.css"/>
- <link rel="stylesheet" type="text/css" href="plugins/CoreHome/stylesheets/jquery.ui.autocomplete.css" />
- <link rel="stylesheet" type="text/css" href="plugins/CoreUpdater/stylesheets/updateLayout.css" />
-
- <script type="text/javascript" src="libs/bower_components/jquery/dist/jquery.min.js"></script>
- <script type="text/javascript" src="libs/bower_components/jquery-ui/ui/minified/jquery-ui.min.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/javascripts/donate.js"></script>
- <script type="text/javascript" src="plugins/CoreUpdater/javascripts/updateLayout.js"></script>
-
- <script type="text/javascript" src="libs/bower_components/angular/angular.min.js"></script>
- <script type="text/javascript" src="libs/bower_components/angular-sanitize/angular-sanitize.js"></script>
- <script type="text/javascript" src="libs/bower_components/angular-animate/angular-animate.js"></script>
- <script type="text/javascript" src="libs/bower_components/angular-cookies/angular-cookies.js"></script>
- <script type="text/javascript" src="libs/bower_components/ngDialog/js/ngDialog.min.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/services/service.module.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/filters/filter.module.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/filters/translate.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/directives/directive.module.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/piwikApp.config.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/piwikApp.js"></script>
- <script type="text/javascript" src="plugins/Installation/javascripts/installation.js"></script>
+ <link rel="stylesheet" type="text/css" href="index.php?module=CoreUpdater&action=getUpdaterCss"/>
+ <script type="text/javascript" src="index.php?module=CoreUpdater&action=getUpdaterJs"></script>
<script type="text/javascript">{{ getJavascriptTranslations()|raw }}</script>
{% include "@CoreHome/_favicon.twig" %}
</head>
-<!--[if (gte IE 9)|!(IE)]><!-->
-<body id="simple" ng-app="app"><!--<![endif]-->
+<body id="simple" ng-app="app">
<div class="logo">
<img title="Piwik" alt="Piwik" src="{{ logoHeader }}"/>
diff --git a/plugins/CoreUpdater/templates/newVersionAvailable.twig b/plugins/CoreUpdater/templates/newVersionAvailable.twig
index 8da941c81f..348b78923b 100644
--- a/plugins/CoreUpdater/templates/newVersionAvailable.twig
+++ b/plugins/CoreUpdater/templates/newVersionAvailable.twig
@@ -22,9 +22,9 @@
<form id="oneclickupdate" action="index.php">
<input type="hidden" name="module" value="CoreUpdater"/>
<input type="hidden" name="action" value="oneClickUpdate"/>
- <input id="updateAutomatically" type="submit" class="btn btn-lg" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
+ <input id="updateAutomatically" type="submit" class="btn" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
{% endif %}
- <a class="btn btn-lg"
+ <a class="btn"
href="{{ piwik_latest_version_url }}?cb={{ piwik_new_version }}">{{ 'CoreUpdater_DownloadX'|translate(piwik_new_version) }}</a><br/>
{% if can_auto_update %}
</form>
diff --git a/plugins/CoreUpdater/templates/runUpdaterAndExit_welcome.twig b/plugins/CoreUpdater/templates/runUpdaterAndExit_welcome.twig
index 892099eb46..168524487b 100644
--- a/plugins/CoreUpdater/templates/runUpdaterAndExit_welcome.twig
+++ b/plugins/CoreUpdater/templates/runUpdaterAndExit_welcome.twig
@@ -8,7 +8,7 @@
{{ postEvent('Template.topBar')|raw }}
</div>
- <div class="content text-left">
+ <div class="content" style="text-align:left;">
{% set helpMessage %}
{{ 'CoreUpdater_HelpMessageContent'|translate('<a target="_blank" href="?module=Proxy&action=redirect&url=http://piwik.org/faq/">','</a>','</li><li>')|raw }}
@@ -75,9 +75,9 @@
<form action="index.php" id="upgradeCorePluginsForm" class="clearfix" data-updating="{{ 'CoreUpdater_Updating'|translate }}...">
<input type="hidden" name="updateCorePlugins" value="1"/>
{% if queries|length == 1 %}
- <input type="submit" class="submit" value="{{ 'General_ContinueToPiwik'|translate }}"/>
+ <input type="submit" class="btn right" value="{{ 'General_ContinueToPiwik'|translate }}"/>
{% else %}
- <input type="submit" class="submit" value="{{ 'CoreUpdater_UpgradePiwik'|translate }}"/>
+ <input type="submit" class="btn right" value="{{ 'CoreUpdater_UpgradePiwik'|translate }}"/>
{% endif %}
</form>
{% else %}
@@ -87,7 +87,7 @@
</div>
{% endif %}
<form action="index.php" class="clearfix">
- <input type="submit" class="submit" value="{{ 'General_ContinueToPiwik'|translate }}"/>
+ <input type="submit" class="btn right" value="{{ 'General_ContinueToPiwik'|translate }}"/>
</form>
{% endif %}
{% endif %}
diff --git a/plugins/CoreUpdater/templates/updateHttpsError.twig b/plugins/CoreUpdater/templates/updateHttpsError.twig
index 399908e1f6..e8518fdf80 100644
--- a/plugins/CoreUpdater/templates/updateHttpsError.twig
+++ b/plugins/CoreUpdater/templates/updateHttpsError.twig
@@ -6,7 +6,7 @@
<h1>{{ 'CoreUpdater_UpdateErrorTitle'|translate }}</h1>
</div>
- <div class="content text-left">
+ <div class="content" style="text-align:left;">
<div class="alert alert-warning">
{{ 'CoreUpdater_UpdateUsingHttpsFailed'|translate }}<br/>
@@ -21,7 +21,7 @@
<input type="hidden" name="module" value="CoreUpdater"/>
<input type="hidden" name="action" value="oneClickUpdate"/>
<input type="hidden" name="https" value="1"/>
- <input id="updateUsingHttps" type="submit" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
+ <input id="updateUsingHttps" class="btn" type="submit" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
{{ 'CoreUpdater_UsingHttps'|translate }}
</form>
<br/>
@@ -30,13 +30,13 @@
<input type="hidden" name="module" value="CoreUpdater"/>
<input type="hidden" name="action" value="oneClickUpdate"/>
<input type="hidden" name="https" value="0"/>
- <input id="updateUsingHttp" type="submit" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
+ <input id="updateUsingHttp" class="btn" type="submit" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
{{ 'CoreUpdater_UsingHttp'|translate }}
</form>
<br/>
<form action="index.php">
- <input type="submit" value="{{ 'General_ContinueToPiwik'|translate }}"/>
+ <input type="submit" class="btn" value="{{ 'General_ContinueToPiwik'|translate }}"/>
</form>
</div>
diff --git a/plugins/CoreUpdater/templates/updateSuccess.twig b/plugins/CoreUpdater/templates/updateSuccess.twig
index 3ce45af298..3c930e0f42 100644
--- a/plugins/CoreUpdater/templates/updateSuccess.twig
+++ b/plugins/CoreUpdater/templates/updateSuccess.twig
@@ -23,11 +23,11 @@
</h2>
<div class="row">
- <div class="col-sm-5 col-sm-offset-1">
- <a href="https://piwik.pro/enterprise?pk_medium=App_Enterprise_button&pk_source=Piwik_App&pk_campaign=App_Updated" class="btn btn-lg btn-block">{{ 'CoreUpdater_EnterpriseSolutions'|translate }}</a>
+ <div class="col s5 offset-s1">
+ <a href="https://piwik.pro/enterprise?pk_medium=App_Enterprise_button&pk_source=Piwik_App&pk_campaign=App_Updated" class="btn btn-block">{{ 'CoreUpdater_EnterpriseSolutions'|translate }}</a>
</div>
- <div class="col-sm-5">
- <a href="https://piwik.pro/cloud?pk_medium=App_Cloud_button&pk_source=Piwik_App&pk_campaign=App_Updated" class="btn btn-lg btn-block">{{ 'CoreUpdater_CloudHosting'|translate }}</a>
+ <div class="col s5">
+ <a href="https://piwik.pro/cloud?pk_medium=App_Cloud_button&pk_source=Piwik_App&pk_campaign=App_Updated" class="btn btn-block">{{ 'CoreUpdater_CloudHosting'|translate }}</a>
</div>
</div>
diff --git a/plugins/CoreVisualizations/Visualizations/Cloud.php b/plugins/CoreVisualizations/Visualizations/Cloud.php
index 502411e74c..ff9888c9eb 100644
--- a/plugins/CoreVisualizations/Visualizations/Cloud.php
+++ b/plugins/CoreVisualizations/Visualizations/Cloud.php
@@ -26,7 +26,7 @@ class Cloud extends Visualization
{
const ID = 'cloud';
const TEMPLATE_FILE = "@CoreVisualizations/_dataTableViz_tagCloud.twig";
- const FOOTER_ICON = 'plugins/Morpheus/images/tagcloud.png';
+ const FOOTER_ICON = 'icon-tag-cloud';
const FOOTER_ICON_TITLE = 'General_TagCloud';
/** Used by system tests to make sure output is consistent. */
diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable.php b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
index dff8aa3e7a..65866c8edf 100644
--- a/plugins/CoreVisualizations/Visualizations/HtmlTable.php
+++ b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
@@ -23,7 +23,7 @@ class HtmlTable extends Visualization
{
const ID = 'table';
const TEMPLATE_FILE = "@CoreVisualizations/_dataTableViz_htmlTable.twig";
- const FOOTER_ICON = 'plugins/Morpheus/images/table.png';
+ const FOOTER_ICON = 'icon-table';
const FOOTER_ICON_TITLE = 'General_DisplaySimpleTable';
public static function getDefaultConfig()
diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php
index 959aa0d57b..4065fd10f7 100644
--- a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php
+++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php
@@ -20,7 +20,7 @@ use Piwik\View;
class AllColumns extends HtmlTable
{
const ID = 'tableAllColumns';
- const FOOTER_ICON = 'plugins/Morpheus/images/table_more.png';
+ const FOOTER_ICON = 'icon-table-more';
const FOOTER_ICON_TITLE = 'General_DisplayTableWithMoreMetrics';
public function beforeRender()
diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php
index 121cfdc315..94cfa60050 100644
--- a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php
+++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php
@@ -18,7 +18,7 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
class Bar extends JqplotGraph
{
const ID = 'graphVerticalBar';
- const FOOTER_ICON = 'plugins/Morpheus/images/chart_bar.png';
+ const FOOTER_ICON = 'icon-chart-bar';
const FOOTER_ICON_TITLE = 'General_VBarGraph';
public function beforeRender()
diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php
index bd2d033fae..3c43bb31a1 100644
--- a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php
+++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php
@@ -18,7 +18,7 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
class Pie extends JqplotGraph
{
const ID = 'graphPie';
- const FOOTER_ICON = 'plugins/Morpheus/images/chart_pie.png';
+ const FOOTER_ICON = 'icon-chart-pie';
const FOOTER_ICON_TITLE = 'General_Piechart';
public static function getDefaultConfig()
diff --git a/plugins/CoreVisualizations/Visualizations/Sparklines.php b/plugins/CoreVisualizations/Visualizations/Sparklines.php
index 3b576c885e..fa081d55d5 100644
--- a/plugins/CoreVisualizations/Visualizations/Sparklines.php
+++ b/plugins/CoreVisualizations/Visualizations/Sparklines.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
+use Piwik\Common;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Plugin\ViewDataTable;
@@ -70,6 +71,12 @@ class Sparklines extends ViewDataTable
}
$view->sparklines = $this->config->getSortedSparklines();
+ $view->isWidget = Common::getRequestVar('widget', 0, 'int');
+
+ $view->title = '';
+ if ($_GET['showtitle'] === '1') {
+ $view->title = $this->config->title;
+ }
return $view->render();
}
diff --git a/plugins/CoreVisualizations/javascripts/jqplot.js b/plugins/CoreVisualizations/javascripts/jqplot.js
index 6e5d2135d1..5f51e7e341 100644
--- a/plugins/CoreVisualizations/javascripts/jqplot.js
+++ b/plugins/CoreVisualizations/javascripts/jqplot.js
@@ -178,7 +178,9 @@
// manage resources
target.on('piwikDestroyPlot', function () {
- $(window).off('resize', this._resizeListener);
+ if (this._resizeListener) {
+ $(window).off('resize', this._resizeListener);
+ }
self._plot.destroy();
for (var i = 0; i < $.jqplot.visiblePlots.length; i++) {
if ($.jqplot.visiblePlots[i] == self._plot) {
@@ -385,7 +387,9 @@
// TODO: this code destroys plots when a page is switched. there must be a better way of managing memory.
if (typeof $.jqplot.visiblePlots == 'undefined') {
$.jqplot.visiblePlots = [];
- $('#secondNavBar').on('piwikSwitchPage', function () {
+ var $rootScope = piwikHelper.getAngularDependency('$rootScope');
+
+ $rootScope.$on('piwikPageChange', function () {
for (var i = 0; i < $.jqplot.visiblePlots.length; i++) {
if ($.jqplot.visiblePlots[i] == null) {
continue;
@@ -949,7 +953,7 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function () {
// legend will be put there
if (this.plugins.canvasLegend.show) {
options.gridPadding = {
- top: 21
+ top: 21, right: 0
};
}
diff --git a/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less b/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less
index 589278a6f4..ff2118b952 100644
--- a/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less
+++ b/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less
@@ -1,17 +1,3 @@
-/* container of each table */
-.dataTableVizHtmlTable > .dataTableWrapper {
- width: 450px;
- /* not more than 450px to make sure 2 tables can fit horizontally on a 1024 screen */
-}
-
-.dataTableVizAllColumns > .dataTableWrapper {
- width: 535px;
-}
-
-.dataTableVizPie > .dataTableWrapper, .dataTableVizBar > .dataTableWrapper {
- width: 500px;
- min-height: 1px;
-}
.piwik-graph {
height: 250px;
@@ -25,4 +11,16 @@
.piwik-graph {
height: 170px;
}
+}
+
+.widget .dataTableVizEvolution {
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+.widget .tagCloud {
+ padding: 10px;
+}
+.widget .dataTableVizBar .jqplot-graph {
+ padding: 0 10px 10px 10px;
} \ No newline at end of file
diff --git a/plugins/CoreVisualizations/stylesheets/jqplot.css b/plugins/CoreVisualizations/stylesheets/jqplot.css
index f074320c43..025c179847 100644
--- a/plugins/CoreVisualizations/stylesheets/jqplot.css
+++ b/plugins/CoreVisualizations/stylesheets/jqplot.css
@@ -76,12 +76,11 @@
.rowevolution {
position: relative;
- overflow: hidden;
text-align: left;
}
#Piwik_Popover .rowevolution .alert-info {
- margin-left: 0px;
+ margin-left: 0;
}
a.rowevolution-startmulti {
@@ -118,6 +117,11 @@ a.rowevolution-startmulti {
-khtml-user-select: none; /* Webkit (Safari, Chrome) */
}
+.rowevolution table.metrics td.sparkline,
+.multirowevolution table.metrics td.sparkline {
+ float:none;
+}
+
.rowevolution table.metrics tr {
margin: 0;
padding: 0;
diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
index 38a0b9d97e..4ed2dbc655 100644
--- a/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
+++ b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
@@ -1,34 +1,41 @@
{% import '@CoreVisualizations/macros.twig' as macros %}
{% if not isWidget %}
-<div class="row">
- <div class="col-md-6">
+ <div class="card"><div class="card-content">
{% endif %}
-
- {% for key, sparkline in sparklines %}
- {% if key is even %}
- {{ macros.singleSparkline(sparkline) }}
- {% endif %}
- {% endfor %}
-
-{% if not isWidget %}
- <br style="clear:left"/>
+ {% if title is not empty %}<h2 class="card-title">{{ title }}</h2>{% endif %}
+
+ {% if not isWidget %}
+ <div class="row">
+ <div class="col m6">
+ {% endif %}
+
+ {% for key, sparkline in sparklines %}
+ {% if key is even %}
+ {{ macros.singleSparkline(sparkline) }}
+ {% endif %}
+ {% endfor %}
+
+ {% if not isWidget %}
+ <br style="clear:left"/>
+ </div>
+ <div class="col m6">
+ {% endif %}
+
+ {% for key, sparkline in sparklines %}
+ {% if key is odd %}
+ {{ macros.singleSparkline(sparkline) }}
+ {% endif %}
+ {% endfor %}
+
+ <br style="clear:left"/>
+
+ {% if not isWidget %}
+ </div>
</div>
- <div class="col-md-6">
-{% endif %}
-
- {% for key, sparkline in sparklines %}
- {% if key is odd %}
- {{ macros.singleSparkline(sparkline) }}
- {% endif %}
- {% endfor %}
-
- <br style="clear:left"/>
+ {% endif %}
+ {% include "_sparklineFooter.twig" %}
{% if not isWidget %}
- </div>
-</div>
-{% endif %}
-
-{% include "_sparklineFooter.twig" %}
-
+ </div></div>
+{% endif %} \ No newline at end of file
diff --git a/plugins/CustomAlerts b/plugins/CustomAlerts
-Subproject 3a2a1e8b62a12b22827a01c68a68d0290da823b
+Subproject d239c89269841a2f31452c17c1782bd4a695bf3
diff --git a/plugins/CustomDimensions b/plugins/CustomDimensions
-Subproject 10920d1414254a9fceaf2cfb3a3cfa46b29dc1b
+Subproject ecba4e4bb9eaf8196dd98316ace28a5a63db5b4
diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html
index 6ab9ea6ba6..96b61a92cb 100644
--- a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html
+++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html
@@ -1,54 +1,62 @@
<div class="manageCustomVars">
- <h2 piwik-enriched-headline help-url="http://piwik.org/docs/custom-variables/">{{ 'CustomVariables_CustomVariables'|translate }}</h2>
+ <div piwik-content-intro>
+ <h2 piwik-enriched-headline
+ help-url="http://piwik.org/docs/custom-variables/">
+ {{ 'CustomVariables_CustomVariables'|translate }}
+ </h2>
- <p>
- <span ng-bind-html="'CustomVariables_ManageDescription'|translate:manageCustomVars.siteName"></span>
- </p>
+ <p>
+ <span ng-bind-html="'CustomVariables_ManageDescription'|translate:manageCustomVars.siteName"></span>
+ </p>
+ </div>
<div class="alert alert-info" ng-show="!manageCustomVars.model.isLoading && manageCustomVars.model.hasCustomVariablesInGeneral && !manageCustomVars.model.hasAtLeastOneUsage">
{{ 'CustomVariables_SlotsReportIsGeneratedOverTime'|translate }}
</div>
<div ng-repeat="scope in manageCustomVars.scopes">
- <h2 class="secondary">{{ 'CustomVariables_ScopeX'|translate:(scope.name) }}</h2>
- <table class="dataTable entityTable">
- <thead>
- <tr>
- <th>{{'CustomVariables_Index'|translate }}</th>
- <th>{{'CustomVariables_Usages'|translate }}</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td colspan="3" ng-show="manageCustomVars.model.isLoading">{{ 'General_Loading'|translate }}</td>
- </tr>
- <tr ng-repeat="customVariables in manageCustomVars.model.customVariables|filter:{scope: scope.value}">
- <td class="index">{{ customVariables.index }}</td>
- <td>
+ <div piwik-content-block content-title="{{ 'CustomVariables_ScopeX'|translate:(scope.name) }}">
+ <table piwik-content-table>
+ <thead>
+ <tr>
+ <th>{{'CustomVariables_Index'|translate }}</th>
+ <th>{{'CustomVariables_Usages'|translate }}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td colspan="3" ng-show="manageCustomVars.model.isLoading">{{ 'General_Loading'|translate }}</td>
+ </tr>
+ <tr ng-repeat="customVariables in manageCustomVars.model.customVariables|filter:{scope: scope.value}">
+ <td class="index">{{ customVariables.index }}</td>
+ <td>
<span ng-show="(customVariables.usages|length) === 0"
class="unused">{{'CustomVariables_Unused'|translate }}</span>
<span ng-show="customVariables.usages|length" ng-repeat="cvar in customVariables.usages|orderBy:'-nb_actions'">
<span title="{{ 'CustomVariables_UsageDetails'|translate:(cvar.nb_visits ? cvar.nb_visits : 0):(cvar.nb_actions ? cvar.nb_actions : 0) }}">{{ cvar.name }}</span><span ng-show="!$last">, </span>
</span>
- </td>
- </tr>
- </tbody>
- </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
- <h2 class="secondary" ng-show="!manageCustomVars.model.isLoading">{{ 'CustomVariables_CreateNewSlot'|translate }}</h2>
+ <div ng-show="!manageCustomVars.model.isLoading"
+ piwik-content-block
+ content-title="{{ 'CustomVariables_CreateNewSlot'|translate }}">
- <p ng-show="!manageCustomVars.model.isLoading">
- {{ 'CustomVariables_CreatingCustomVariableTakesTime'|translate }}
- <br /><br />
- <span ng-bind-html="'CustomVariables_CurrentAvailableCustomVariables'|translate:('<strong>'+manageCustomVars.model.numSlotsAvailable+'</strong>')"></span>
- <br />
- <br />
- {{ 'CustomVariables_ToCreateCustomVarExecute'|translate }}
- <br />
- <br />
- <code>./console customvariables:set-max-custom-variables {{ manageCustomVars.model.numSlotsAvailable + 1 }}</code>
- </p>
+ <p ng-show="!manageCustomVars.model.isLoading">
+ {{ 'CustomVariables_CreatingCustomVariableTakesTime'|translate }}
+ <br /><br />
+ <span ng-bind-html="'CustomVariables_CurrentAvailableCustomVariables'|translate:('<strong>'+manageCustomVars.model.numSlotsAvailable+'</strong>')"></span>
+ <br />
+ <br />
+ {{ 'CustomVariables_ToCreateCustomVarExecute'|translate }}
+ <br />
+ <pre piwik-select-on-focus><code>./console customvariables:set-max-custom-variables {{ manageCustomVars.model.numSlotsAvailable + 1 }}</code></pre>
+ </p>
+ </div>
</div> \ No newline at end of file
diff --git a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png
index a1ff59836d..eb77f36985 100644
--- a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png
+++ b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png
Binary files differ
diff --git a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png
index 17c3b9bd72..3d7c2233e3 100644
--- a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png
+++ b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png
Binary files differ
diff --git a/plugins/DBStats/Controller.php b/plugins/DBStats/Controller.php
index 8db31cf4df..b8bfebbffa 100644
--- a/plugins/DBStats/Controller.php
+++ b/plugins/DBStats/Controller.php
@@ -29,6 +29,8 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$view = new View('@DBStats/index');
$this->setBasicVariablesView($view);
+ $_GET['showtitle'] = '1';
+
$view->databaseUsageSummary = $this->renderReport('getDatabaseUsageSummary');
$view->trackerDataSummary = $this->renderReport('getTrackerDataSummary');
$view->metricDataSummary = $this->renderReport('getMetricDataSummary');
diff --git a/plugins/DBStats/DBStats.php b/plugins/DBStats/DBStats.php
index e5d6f85647..76da2ac82e 100644
--- a/plugins/DBStats/DBStats.php
+++ b/plugins/DBStats/DBStats.php
@@ -23,16 +23,10 @@ class DBStats extends \Piwik\Plugin
public function registerEvents()
{
return array(
- 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
- "TestingEnvironment.addHooks" => 'setupTestEnvironment'
+ "TestingEnvironment.addHooks" => 'setupTestEnvironment'
);
}
- public function getStylesheetFiles(&$stylesheets)
- {
- $stylesheets[] = "plugins/DBStats/stylesheets/dbStatsTable.less";
- }
-
public function setupTestEnvironment($environment)
{
Piwik::addAction("MySQLMetadataProvider.createDao", function (&$dao) {
diff --git a/plugins/DBStats/Reports/GetAdminDataSummary.php b/plugins/DBStats/Reports/GetAdminDataSummary.php
index cec185cc85..402b774480 100644
--- a/plugins/DBStats/Reports/GetAdminDataSummary.php
+++ b/plugins/DBStats/Reports/GetAdminDataSummary.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\DBStats\Reports;
+use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
@@ -21,6 +22,11 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
class GetAdminDataSummary extends Base
{
+ protected function init()
+ {
+ $this->name = Piwik::translate('DBStats_OtherTables');
+ }
+
public function configureView(ViewDataTable $view)
{
$this->addBaseDisplayProperties($view);
diff --git a/plugins/DBStats/Reports/GetDatabaseUsageSummary.php b/plugins/DBStats/Reports/GetDatabaseUsageSummary.php
index 4e588aa311..cecabc0a04 100644
--- a/plugins/DBStats/Reports/GetDatabaseUsageSummary.php
+++ b/plugins/DBStats/Reports/GetDatabaseUsageSummary.php
@@ -19,6 +19,12 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Pie;
*/
class GetDatabaseUsageSummary extends Base
{
+
+ protected function init()
+ {
+ $this->name = Piwik::translate('General_Overview');
+ }
+
public function getDefaultTypeViewDataTable()
{
return Pie::ID;
diff --git a/plugins/DBStats/Reports/GetIndividualMetricsSummary.php b/plugins/DBStats/Reports/GetIndividualMetricsSummary.php
index 598ab723d0..a36f742c66 100644
--- a/plugins/DBStats/Reports/GetIndividualMetricsSummary.php
+++ b/plugins/DBStats/Reports/GetIndividualMetricsSummary.php
@@ -21,6 +21,12 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
*/
class GetIndividualMetricsSummary extends Base
{
+
+ protected function init()
+ {
+ $this->name = Piwik::translate('General_Metrics');
+ }
+
public function configureView(ViewDataTable $view)
{
$this->addBaseDisplayProperties($view);
diff --git a/plugins/DBStats/Reports/GetIndividualReportsSummary.php b/plugins/DBStats/Reports/GetIndividualReportsSummary.php
index a7ef06af09..7f224c7909 100644
--- a/plugins/DBStats/Reports/GetIndividualReportsSummary.php
+++ b/plugins/DBStats/Reports/GetIndividualReportsSummary.php
@@ -21,6 +21,12 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
*/
class GetIndividualReportsSummary extends Base
{
+
+ protected function init()
+ {
+ $this->name = Piwik::translate('General_Reports');
+ }
+
public function configureView(ViewDataTable $view)
{
$this->addBaseDisplayProperties($view);
diff --git a/plugins/DBStats/Reports/GetTrackerDataSummary.php b/plugins/DBStats/Reports/GetTrackerDataSummary.php
index cc4e853837..5e94c90784 100644
--- a/plugins/DBStats/Reports/GetTrackerDataSummary.php
+++ b/plugins/DBStats/Reports/GetTrackerDataSummary.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\DBStats\Reports;
+use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
@@ -17,6 +18,11 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
*/
class GetTrackerDataSummary extends Base
{
+ protected function init()
+ {
+ $this->name = Piwik::translate('DBStats_TrackerTables');
+ }
+
public function configureView(ViewDataTable $view)
{
$this->addBaseDisplayProperties($view);
diff --git a/plugins/DBStats/stylesheets/dbStatsTable.less b/plugins/DBStats/stylesheets/dbStatsTable.less
deleted file mode 100644
index 601c769580..0000000000
--- a/plugins/DBStats/stylesheets/dbStatsTable.less
+++ /dev/null
@@ -1,25 +0,0 @@
-.dbstatsTable {
- display: inline-block;
-}
-
-.dbstatsTable > tbody > tr > td:first-child {
- width: 550px;
-}
-
-.dbstatsTable h2 {
- width: 500px;
-}
-
-.adminTable.dbstatsTable a {
- color: black;
- text-decoration: underline;
-}
-
-.dbstatsTable {
- .dataTable {
- td {
- padding-top: 7px;
- padding-bottom: 7px;
- }
- }
-} \ No newline at end of file
diff --git a/plugins/DBStats/templates/index.twig b/plugins/DBStats/templates/index.twig
index a28a2a3944..486e4c8528 100755
--- a/plugins/DBStats/templates/index.twig
+++ b/plugins/DBStats/templates/index.twig
@@ -3,29 +3,32 @@
{% set title %}{{ 'DBStats_DatabaseUsage'|translate }}{% endset %}
{% block content %}
+<div piwik-content-intro>
+ <h2 piwik-enriched-headline>
+ {{ title }}
+ </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>")|raw }}
+ </p>
+</div>
-<h2 id="databaseUsageSummary">{{ title }}</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>")|raw }}
- <br/>
- <br/>
-</p>
-<table class="adminTable dbstatsTable">
- <tbody>
- <tr>
- <td>{{ databaseUsageSummary|raw }}</td>
- <td>
- <h3 style="margin-top:0;">{{ 'General_GeneralInformation'|translate }}</h3><br/>
+<div class="row">
+ <div class="col s12 m6">
+ {{ databaseUsageSummary|raw }}
+ {{ trackerDataSummary|raw }}
+ </div>
+ <div class="col s12 m6">
+ <div piwik-content-block content-title="{{ 'General_GeneralInformation'|translate|e('html_attr') }}">
<p style="font-size:1.4em;padding-left:21px;line-height:1.8em;">
- <strong><em>{{ userCount }}</em></strong>&nbsp;{% if userCount == 1 %}{{ 'UsersManager_User'|translate }}{% else %}{{ 'UsersManager_MenuUsers'|translate }}{% endif %}
+ <strong>{{ userCount }}</strong>&nbsp;{% if userCount == 1 %}{{ 'UsersManager_User'|translate }}{% else %}{{ 'UsersManager_MenuUsers'|translate }}{% endif %}
<br/>
- <strong><em>{{ siteCount }}</em></strong>&nbsp;{% if siteCount == 1 %}{{ 'General_Website'|translate }}{% else %}{{ 'Referrers_Websites'|translate }}{% endif %}
- </p><br/>
+ <strong>{{ siteCount }}</strong>&nbsp;{% if siteCount == 1 %}{{ 'General_Website'|translate }}{% else %}{{ 'Referrers_Websites'|translate }}{% endif %}
+ </p>
+ </div>
+ <div piwik-content-block content-title="{{ 'PrivacyManager_DeleteDataSettings'|translate|e('html_attr') }}">
{% set clickDeleteLogSettings %}{{ 'PrivacyManager_DeleteDataSettings'|translate }}{% endset %}
- <h3 style="margin-top:0;">{{ 'PrivacyManager_DeleteDataSettings'|translate }}</h3><br/>
-
<p>
{{ 'PrivacyManager_DeleteDataDescription'|translate }}
<br/>
@@ -33,72 +36,37 @@
{{ 'PrivacyManager_ClickHereSettings'|translate("'"~clickDeleteLogSettings~"'") }}
</a>
</p>
- </td>
- </tr>
- </tbody>
-</table>
-
-<br/>
-
-<table class="adminTable dbstatsTable" id="trackerDataSummary">
- <tbody>
- <tr>
- <td>
- <h2 class="secondary">{{ 'DBStats_TrackerTables'|translate }}</h2>
- {{ trackerDataSummary|raw }}
- </td>
- <td>&nbsp;</td>
- </tr>
- </tbody>
-</table>
-
-<table class="adminTable dbstatsTable" id="reportDataSummary">
- <tbody>
- <tr>
- <td>
- <h2 class="secondary">{{ 'DBStats_ReportTables'|translate }}</h2>
- {{ reportDataSummary|raw }}
- </td>
- <td>
- <h2 class="secondary">{{ 'General_Reports'|translate }}</h2>
-
- <div class="ajaxLoad" action="getIndividualReportsSummary">
- <span class="loadingPiwik"><img src="plugins/Morpheus/images/loading-blue.gif"/>{{ 'General_LoadingData'|translate }}</span>
- </div>
- </td>
- </tr>
- </tbody>
-</table>
+ </div>
+ </div>
+</div>
-<table class="adminTable dbstatsTable" id="metricDataSummary">
- <tbody>
- <tr>
- <td>
- <h2 class="secondary">{{ 'DBStats_MetricTables'|translate }}</h2>
- {{ metricDataSummary|raw }}
- </td>
- <td>
- <h2 class="secondary">{{ 'General_Metrics'|translate }}</h2>
+<div class="row">
+ <div class="col s12 m6">
+ {{ reportDataSummary|raw }}
+ </div>
+ <div class="col s12 m6">
+ <div class="ajaxLoad" action="getIndividualReportsSummary">
+ <span class="loadingPiwik"><img src="plugins/Morpheus/images/loading-blue.gif"/>{{ 'General_LoadingData'|translate }}</span>
+ </div>
+ </div>
+</div>
- <div class="ajaxLoad" action="getIndividualMetricsSummary">
- <span class="loadingPiwik"><img src="plugins/Morpheus/images/loading-blue.gif"/>{{ 'General_LoadingData'|translate }}</span>
- </div>
- </td>
- </tr>
- </tbody>
-</table>
+<div class="row">
+ <div class="col s12 m6">
+ {{ metricDataSummary|raw }}
+ </div>
+ <div class="col s12 m6">
+ <div class="ajaxLoad" action="getIndividualMetricsSummary">
+ <span class="loadingPiwik"><img src="plugins/Morpheus/images/loading-blue.gif"/>{{ 'General_LoadingData'|translate }}</span>
+ </div>
+ </div>
+</div>
-<table class="adminTable dbstatsTable" id="adminDataSummary">
- <tbody>
- <tr>
- <td>
- <h2 class="secondary">{{ 'DBStats_OtherTables'|translate }}</h2>
- {{ adminDataSummary|raw }}
- </td>
- <td>&nbsp;</td>
- </tr>
- </tbody>
-</table>
+<div class="row">
+ <div class="col s12 m6">
+ {{ adminDataSummary|raw }}
+ </div>
+</div>
<script type="text/javascript">
(function ($) {
@@ -112,14 +80,14 @@
ajaxRequest.addParams({
module: 'DBStats',
action: action,
- viewDataTable: 'table'
+ viewDataTable: 'table',
+ showtitle: '1'
}, 'get');
- ajaxRequest.setCallback(
- function (data) {
- $('.loadingPiwik', self).remove();
- $(self).html(data);
- }
- );
+ ajaxRequest.setCallback(function (data) {
+ $('.loadingPiwik', self).remove();
+ $(self).html(data);
+ piwikHelper.compileAngularComponents(self);
+ });
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
});
diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php
index 44814f6800..da7a8507d9 100644
--- a/plugins/Dashboard/Dashboard.php
+++ b/plugins/Dashboard/Dashboard.php
@@ -154,12 +154,12 @@ class Dashboard extends \Piwik\Plugin
[
{"uniqueId":"widgetVisitsSummarygetEvolutionGraphforceView1viewDataTablegraphEvolution","parameters":{"forceView":"1","viewDataTable":"graphEvolution","module":"VisitsSummary","action":"getEvolutionGraph"}},
{"uniqueId":"widgetLivewidget","parameters":{"module":"Live","action":"widget"}},
- {"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDurationviewDataTablecloud","parameters":{"viewDataTable":"cloud","module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}}
+ {"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDuration","parameters":{"viewDataTable":"cloud","module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}}
],
[
' . $topWidget . '
{"uniqueId":"widgetReferrersgetWebsites","parameters":{"module":"Referrers","action":"getWebsites"}},
- {"uniqueId":"widgetVisitTimegetVisitInformationPerServerTimeviewDataTablegraphVerticalBar","parameters":{"viewDataTable": "graphVerticalBar","module":"VisitTime","action":"getVisitInformationPerServerTime"}}
+ {"uniqueId":"widgetVisitTimegetVisitInformationPerServerTime","parameters":{"viewDataTable": "graphVerticalBar","module":"VisitTime","action":"getVisitInformationPerServerTime"}}
],
[
{"uniqueId":"widgetUserCountryMapvisitorMap","parameters":{"module":"UserCountryMap","action":"visitorMap"}},
diff --git a/plugins/Dashboard/javascripts/dashboard.js b/plugins/Dashboard/javascripts/dashboard.js
index 582ad1b79a..48c3430824 100644
--- a/plugins/Dashboard/javascripts/dashboard.js
+++ b/plugins/Dashboard/javascripts/dashboard.js
@@ -73,8 +73,9 @@ function showChangeDashboardLayoutDialog() {
var id = makeSelectorLastId('changeDashboardLayout');
piwikHelper.modalConfirm(id, {yes: function () {
- $('#dashboardWidgetsArea').dashboard('setColumnLayout', $(id).find('.choosen').attr('layout'));
- }});
+ var layout = $(id).find('.choosen').attr('layout');
+ $('#dashboardWidgetsArea').dashboard('setColumnLayout', layout);
+ }}, {fixedFooter: true});
}
function showEmptyDashboardNotification() {
diff --git a/plugins/Dashboard/javascripts/dashboardObject.js b/plugins/Dashboard/javascripts/dashboardObject.js
index 3bdd50662c..13862ecd5b 100644
--- a/plugins/Dashboard/javascripts/dashboardObject.js
+++ b/plugins/Dashboard/javascripts/dashboardObject.js
@@ -6,6 +6,8 @@
*/
(function ($) {
+ var layoutColumnSelector = '#dashboardWidgetsArea > .col';
+
/**
* Current dashboard column layout
* @type {object}
@@ -80,14 +82,14 @@
*
* @param {int} dashboardIdToLoad
*/
- loadDashboard: function (dashboardIdToLoad) {
+ loadDashboard: function (dashboardIdToLoad, forceReload) {
$(dashboardElement).empty();
dashboardName = '';
dashboardLayout = null;
dashboardId = dashboardIdToLoad;
- if (piwikHelper.isAngularRenderingThePage()) {
+ if (!forceReload && piwikHelper.isAngularRenderingThePage()) {
angular.element(document).injector().invoke(function ($location) {
$location.search('subcategory', '' + dashboardIdToLoad);
});
@@ -175,7 +177,7 @@
ajaxRequest.withTokenInUrl();
ajaxRequest.setCallback(
function () {
- methods.loadDashboard.apply(this, [dashboardId])
+ methods.loadDashboard.apply(this, [dashboardId, true])
}
);
ajaxRequest.setLoadingElement();
@@ -314,7 +316,7 @@
var columnWidth = layout.split('-');
var columnCount = columnWidth.length;
- var currentCount = $('.col', dashboardElement).length;
+ var currentCount = $('> .col', dashboardElement).length;
if (currentCount < columnCount) {
$('.menuClear', dashboardElement).remove();
@@ -331,8 +333,8 @@
dashboardLayout.columns.pop();
}
// move widgets to other columns depending on columns height
- $('[widgetId]', $('.col:last')).each(function (id, elem) {
- var cols = $('.col').slice(0, columnCount);
+ $('[widgetId]', $(layoutColumnSelector + ':last')).each(function (id, elem) {
+ var cols = $(layoutColumnSelector).slice(0, columnCount);
var smallestColumn = $(cols[0]);
var smallestColumnHeight = null;
cols.each(function (colId, col) {
@@ -345,52 +347,56 @@
$(elem).appendTo(smallestColumn);
});
- $('.col:last').remove();
+ $(layoutColumnSelector + ':last').remove();
}
}
+ var $dashboardElement = $(' > .col', dashboardElement);
+
+ if (!$dashboardElement.size()) {
+ return;
+ }
+
switch (layout) {
case '100':
- $('.col', dashboardElement).removeClass()
- .addClass('col col-sm-12');
+ $dashboardElement.removeClass().addClass('col s12');
break;
case '50-50':
- $('.col', dashboardElement).removeClass()
- .addClass('col col-sm-6');
+ $dashboardElement.removeClass().addClass('col s12 m6');
break;
case '67-33':
- $('.col', dashboardElement)[0].className = 'col col-sm-8';
- $('.col', dashboardElement)[1].className = 'col col-sm-4';
+ $dashboardElement[0].className = 'col s12 m8';
+ $dashboardElement[1].className = 'col s12 m4';
break;
case '33-67':
- $('.col', dashboardElement)[0].className = 'col col-sm-4';
- $('.col', dashboardElement)[1].className = 'col col-sm-8';
+ $dashboardElement[0].className = 'col s12 m4';
+ $dashboardElement[1].className = 'col s12 m8';
break;
case '33-33-33':
- $('.col', dashboardElement)[0].className = 'col col-sm-4';
- $('.col', dashboardElement)[1].className = 'col col-sm-4';
- $('.col', dashboardElement)[2].className = 'col col-sm-4';
+ $dashboardElement[0].className = 'col s12 m4';
+ $dashboardElement[1].className = 'col s12 m4';
+ $dashboardElement[2].className = 'col s12 m4';
break;
case '40-30-30':
- $('.col', dashboardElement)[0].className = 'col col-sm-6';
- $('.col', dashboardElement)[1].className = 'col col-sm-3';
- $('.col', dashboardElement)[2].className = 'col col-sm-3';
+ $dashboardElement[0].className = 'col s12 m6';
+ $dashboardElement[1].className = 'col s12 m3';
+ $dashboardElement[2].className = 'col s12 m3';
break;
case '30-40-30':
- $('.col', dashboardElement)[0].className = 'col col-sm-3';
- $('.col', dashboardElement)[1].className = 'col col-sm-6';
- $('.col', dashboardElement)[2].className = 'col col-sm-3';
+ $dashboardElement[0].className = 'col s12 m3';
+ $dashboardElement[1].className = 'col s12 m6';
+ $dashboardElement[2].className = 'col s12 m3';
break;
case '30-30-40':
- $('.col', dashboardElement)[0].className = 'col col-sm-3';
- $('.col', dashboardElement)[1].className = 'col col-sm-3';
- $('.col', dashboardElement)[2].className = 'col col-sm-6';
+ $dashboardElement[0].className = 'col s12 m3';
+ $dashboardElement[1].className = 'col s12 m3';
+ $dashboardElement[2].className = 'col s12 m6';
break;
case '25-25-25-25':
- $('.col', dashboardElement)[0].className = 'col col-sm-3';
- $('.col', dashboardElement)[1].className = 'col col-sm-3';
- $('.col', dashboardElement)[2].className = 'col col-sm-3';
- $('.col', dashboardElement)[3].className = 'col col-sm-3';
+ $dashboardElement[0].className = 'col s12 m3';
+ $dashboardElement[1].className = 'col s12 m3';
+ $dashboardElement[2].className = 'col s12 m3';
+ $dashboardElement[3].className = 'col s12 m3';
break;
}
@@ -457,16 +463,16 @@
}
// do not try to add widget if given column number is to high
- if (columnNumber > $('.col', dashboardElement).length) {
+ if (columnNumber > $('> .col', dashboardElement).length) {
return;
}
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({
@@ -500,9 +506,9 @@
}
//launch 'sortable' property on every dashboard widgets
- $( "div.col:data('ui-sortable')", dashboardElement ).sortable('destroy');
+ $( layoutColumnSelector + ":data('ui-sortable')", dashboardElement ).sortable('destroy');
- $('div.col', dashboardElement)
+ $('> .col', dashboardElement)
.sortable({
items: 'div.sortable',
opacity: 0.6,
@@ -513,7 +519,7 @@
helper: 'clone',
start: onStart,
stop: onStop,
- connectWith: 'div.col'
+ connectWith: layoutColumnSelector
});
}
@@ -594,7 +600,8 @@
var columns = [];
var columnNumber = 0;
- $('.col').each(function () {
+
+ $(layoutColumnSelector).each(function () {
columns[columnNumber] = [];
var items = $('[widgetId]', this);
for (var j = 0; j < items.size(); j++) {
diff --git a/plugins/Dashboard/javascripts/dashboardWidget.js b/plugins/Dashboard/javascripts/dashboardWidget.js
index b3e78dcea3..995223e86f 100755
--- a/plugins/Dashboard/javascripts/dashboardWidget.js
+++ b/plugins/Dashboard/javascripts/dashboardWidget.js
@@ -122,6 +122,14 @@
$widgetContent.html(loadedContent);
+ /* move widget icons into datatable top actions
+ var $buttons = currentWidget.find('.buttons .button');
+ var $controls = currentWidget.find('.dataTableControls .dataTableAction').first();
+ if ($buttons.size() && $controls.size()) {
+ $buttons.find('.button').addClass('dataTableAction');
+ $buttons.insertBefore($controls);
+ }*/
+
if (currentWidget.parents('body').size()) {
// there might be race conditions, eg widget might be just refreshed while whole dashboard is also
// removed from DOM
@@ -303,8 +311,6 @@
var width = Math.floor($('body').width() * 0.7);
- var isFooterExpanded = $('.dataTableFeatures', this.element).hasClass('expanded');
-
var self = this;
this.element.dialog({
title: '',
@@ -315,9 +321,6 @@
autoOpen: true,
close: function (event, ui) {
self.isMaximised = false;
- if (!isFooterExpanded) {
- $('.dataTableFeatures', self.element).removeClass('expanded');
- }
$('body').off('.dashboardWidget');
$(this).dialog("destroy");
$('[id="' + self.uniqueId + '-placeholder"]').replaceWith(this);
@@ -328,7 +331,6 @@
}
});
this.element.find('div.piwik-graph').trigger('resizeGraph');
- $('.dataTableFeatures', this.element).addClass('expanded');
var currentWidget = this.element;
$('body').on('click.dashboardWidget', function (ev) {
diff --git a/plugins/Dashboard/javascripts/widgetMenu.js b/plugins/Dashboard/javascripts/widgetMenu.js
index 7190f342a5..100674ce00 100644
--- a/plugins/Dashboard/javascripts/widgetMenu.js
+++ b/plugins/Dashboard/javascripts/widgetMenu.js
@@ -292,7 +292,7 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
$('.dashboard-manager .addWidget').outerHeight();
if (!$('#content.admin').length) {
- position += 10; // + padding defined in dashboard view
+ position += 5; // + padding defined in dashboard view
}
$('.' + settings.widgetlistClass, widgetPreview).css('top', position);
diff --git a/plugins/Dashboard/stylesheets/dashboard.less b/plugins/Dashboard/stylesheets/dashboard.less
index 20a5fbac92..7269aa23ed 100644
--- a/plugins/Dashboard/stylesheets/dashboard.less
+++ b/plugins/Dashboard/stylesheets/dashboard.less
@@ -2,13 +2,19 @@
margin: 0 -6px;
}
-#standalone #dashboard {
- margin: -10px -6px 0;
-}
+body#standalone {
+ background-color: @theme-color-widget-exported-background-base;
+ .top_controls {
+ background-color: @theme-color-widget-exported-background-base;
+ }
+ #dashboard {
+ margin: -10px -6px 0;
+ }
+}
#dashboard {
- .col {
+ #dashboardWidgetsArea > .col {
min-height: 100px;
// Customize Bootstrap gutter between columns
padding-right: 6px;
@@ -47,8 +53,6 @@
.ui-confirm {
display: none;
- width: 630px;
- background: @theme-color-background-base;
color: @theme-color-text-light;
cursor: default;
font-size: 12px !important;
@@ -56,10 +60,15 @@
padding: 20px 10px;
border-radius: 4px;
min-height: 0 !important;
+
+ textarea {
+ background-color: @theme-color-background-contrast;
+ }
}
.ui-confirm p {
- margin-top:10px;
+ margin-top: 16px;
+ font-size: 14px;
}
.ui-confirm h2 {
@@ -138,27 +147,6 @@
height: 30px;
}
-.dashboardSettings {
- position: absolute;
- z-index: 120;
- background: #f7f7f7;
- border: 1px solid #e4e5e4;
- border-radius: 4px;
- color: @theme-color-text-light;
- cursor: pointer;
- overflow: hidden;
-}
-
-.dashboardSettings:hover {
- background: #f1f0eb;
- border-color: #a9a399;
-}
-
-.dashboardSettings.expanded {
- z-index: 1020; /* More than .jqplot-seriespicker-popover (1010) */
- min-width: 800px;
-}
-
.dashboardSettings > span {
background: url(plugins/Morpheus/images/sort_subtable_desc.png) right center no-repeat;
padding-right: 20px;
@@ -166,7 +154,6 @@
}
.dashboardSettings ul.submenu {
- padding-top: 5px;
display: none;
float: left;
}
@@ -174,15 +161,17 @@
.dashboardSettings.expanded ul.submenu {
display: block;
list-style: square outside none;
+ z-index: 1020; /* More than .jqplot-seriespicker-popover (1010) */
}
-.dashboardSettings > ul.submenu > li {
- padding: 5px 0;
- clear: both;
+.dashboardSettings.expanded .dropdown {
+ min-width: 800px;
+ background-color: @theme-color-background-contrast;
}
-.dashboardSettings > ul.submenu > li:hover {
- color:@theme-color-link;
+.dashboardSettings ul.submenu > li {
+ padding: 5px 0;
+ clear: both;
}
#changeDashboardLayout h2 {
@@ -257,8 +246,6 @@
.widgetpreview-base {
clear: both;
- min-height: 600px;
- -height: 600px;
}
.addWidget, .manageDashboard {
@@ -308,14 +295,8 @@ ul.widgetpreview-widgetlist {
}
div.widgetpreview-preview {
- float: left;
width: 500px;
-}
-
-.dashboardSettings {
- min-height: 0;
- height: auto;
- margin-right: 10px;
+ float: left;
}
.dashboardSettings .submenu {
@@ -339,10 +320,18 @@ div.widgetpreview-preview {
line-height: 20px;
padding: 0 25px 0 5px;
color: @theme-color-text-light;
+ width: 200px;
}
-.dashboardSettings .submenu ul li:hover {
+.dashboardSettings ul.submenu > li.generalAction:hover {
color: @theme-color-link;
+ cursor:pointer;
+}
+
+.dashboardSettings .submenu ul li:not([disabled]):hover {
+ background: @theme-color-background-base;
+ border-radius: 2px;
+ cursor:pointer;
}
.dashboardSettings .submenu li[disabled],
@@ -367,6 +356,7 @@ div.widgetpreview-preview {
.dashboardSettings.expanded .widgetpreview-widgetlist,
.dashboardSettings.expanded .widgetpreview-preview {
display: block;
+ z-index: 333999;
}
.widgetPlaceholder {
@@ -392,20 +382,15 @@ div.widgetpreview-preview {
}
#copyDashboardToUserConfirm .inputs {
- width: 375px;
- margin: 10px auto 0;
-}
+ width: 400px;
+ margin: 32px auto 0;
-#copyDashboardToUserConfirm .inputs select,
-#copyDashboardToUserConfirm .inputs input {
- width: 150px;
- float: left;
+ label {
+ margin-top: 18px;
+ display: inline-block;
+ }
}
-#copyDashboardToUserConfirm .inputs label {
- width: 200px;
- float: left;
-}
#dashboardWidgetsArea {
margin-top: -5px;
diff --git a/plugins/Dashboard/stylesheets/standalone.css b/plugins/Dashboard/stylesheets/standalone.css
index 292d3e63c0..142abced53 100644
--- a/plugins/Dashboard/stylesheets/standalone.css
+++ b/plugins/Dashboard/stylesheets/standalone.css
@@ -3,6 +3,10 @@ body {
padding-left: 7px;
}
+#standalone strong {
+ font-weight: 700;
+}
+
#dashboard {
margin: 30px -6px 0 -12px;
width: 100%;
@@ -17,6 +21,9 @@ body {
position: relative;
}
+#Dashboard > ul > li {
+ list-style: square inside none;
+}
#Dashboard > ul {
list-style: square inside none;
background: #f7f7f7;
diff --git a/plugins/Dashboard/stylesheets/widget.less b/plugins/Dashboard/stylesheets/widget.less
index c6743eb559..8127de0547 100644
--- a/plugins/Dashboard/stylesheets/widget.less
+++ b/plugins/Dashboard/stylesheets/widget.less
@@ -2,9 +2,13 @@
.font-default(13px, 18px);
background: @theme-color-widget-background;
border: 1px solid @theme-color-widget-border;
- box-shadow: 0 1px 1px rgba(204,204,204,.5);
- overflow: hidden;
- z-index: 1;
+ border-radius: 2px;
+ position: relative;
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.14),0 1px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.12);
+
+ &:hover, &:focus {
+ box-shadow: 0 1px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
+ }
h2 {
font-weight: normal;
@@ -39,7 +43,7 @@
padding-bottom: 4px;
background: @theme-color-widget-title-background;
h3 {
- .font-default(15px, 18px);
+ .font-default(18px, 18px);
color: @theme-color-widget-title-text;
}
.button {
@@ -71,16 +75,27 @@
}
.pk-emptyDataTable {
- .font-default(12px, 16px);
+ .font-default(13px, 16px);
text-transform: none;
}
- .dataTableFooterIcons {
- border-top: 1px solid @color-silver-l85;
- }
+ .widgetContent {
+ .widgetBody {
+ padding: 5px 12px 0;
+ }
+
+ /** We do not want to show a material-card in a widget which is already a card */
+ .card {
+ box-shadow: 0 0;
- .widgetContent .jqplot-graph {
- margin-top: 6px;
+ .card-content {
+ padding: 0;
+ border-radius: 0
+ }
+ }
+ .jqplot-graph {
+ margin-top: 6px;
+ }
}
.widgetContent.hidden {
@@ -106,8 +121,8 @@
}
.card {
- margin: 0px;
- border-radius: 0px;
+ margin: 0;
+ border-radius: 0;
}
}
@@ -125,6 +140,10 @@
background-color: @theme-color-widget-background;
}
+.widget .datatableFooterMessage {
+ padding-left: 12px;
+}
+
.bar-graph-colors[data-name=grid-background] {
color: @theme-color-widget-background !important;
}
diff --git a/plugins/Dashboard/templates/_dashboardSettings.twig b/plugins/Dashboard/templates/_dashboardSettings.twig
index db21def819..9dc4bd2458 100644
--- a/plugins/Dashboard/templates/_dashboardSettings.twig
+++ b/plugins/Dashboard/templates/_dashboardSettings.twig
@@ -1,22 +1,24 @@
<a class="title" title="{{ 'Dashboard_ManageDashboard'|translate|e('html_attr') }}" tabindex="4"><span class="icon icon-arrow-bottom"></span>{{ 'Dashboard_Dashboard'|translate }} </a>
-<ul class="dropdown submenu">
- <li>
- <div class="addWidget">{{ 'Dashboard_AddAWidget'|translate }}</div>
- <ul class="widgetpreview-categorylist"></ul>
- </li>
- {% if dashboardActions|length > 0 %}
- <li>
- <div class="manageDashboard">{{ 'Dashboard_ManageDashboard'|translate }}</div>
- <ul>
- {% for action, title in dashboardActions %}
- <li data-action="{{ action }}">{{ title|translate }}</li>
- {% endfor %}
- </ul>
- </li>
- {% endif %}
- {% for action, title in generalActions %}
- <li data-action="{{ action }}">{{ title|translate }}</li>
- {% endfor %}
-</ul>
-<ul class="widgetpreview-widgetlist"></ul>
-<div class="widgetpreview-preview"></div> \ No newline at end of file
+<div class="dropdown positionInViewport">
+ <ul class="submenu">
+ <li>
+ <div class="addWidget">{{ 'Dashboard_AddAWidget'|translate }}</div>
+ <ul class="widgetpreview-categorylist"></ul>
+ </li>
+ {% if dashboardActions|length > 0 %}
+ <li>
+ <div class="manageDashboard">{{ 'Dashboard_ManageDashboard'|translate }}</div>
+ <ul>
+ {% for action, title in dashboardActions %}
+ <li data-action="{{ action }}">{{ title|translate }}</li>
+ {% endfor %}
+ </ul>
+ </li>
+ {% endif %}
+ {% for action, title in generalActions %}
+ <li class="generalAction" data-action="{{ action }}">{{ title|translate }}</li>
+ {% endfor %}
+ </ul>
+ <ul class="widgetpreview-widgetlist"></ul>
+ <div class="widgetpreview-preview"></div>
+</div>
diff --git a/plugins/Dashboard/templates/embeddedIndex.twig b/plugins/Dashboard/templates/embeddedIndex.twig
index a7633ca193..77d6fe4bb3 100644
--- a/plugins/Dashboard/templates/embeddedIndex.twig
+++ b/plugins/Dashboard/templates/embeddedIndex.twig
@@ -2,7 +2,7 @@
<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="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
<div class="ui-confirm" id="setAsDefaultWidgetsConfirm">
@@ -10,13 +10,13 @@
{% set resetDashboard %}{{ 'Dashboard_ResetDashboard'|translate }}{% endset %}
<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="no" type="button" value="{{ 'General_No'|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 }}"/>
+ <input role="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
<div class="ui-confirm" id="dashboardEmptyNotification">
@@ -36,15 +36,18 @@
{% endfor %}
</div>
{% endfor %}
+ <br class="clearfix" />
</div>
<input role="yes" type="button" value="{{ 'General_Save'|translate }}"/>
+ <input role="cancel" type="button" value="{{ 'General_Cancel'|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 id="newDashboardNameInput">
+ <label for="newDashboardName">{{ 'Dashboard_DashboardName'|translate }} </label>
+ <input type="text" name="newDashboardName" id="newDashboardName" value=""/>
</div>
<input role="yes" type="button" value="{{ 'General_Save'|translate }}"/>
<input role="cancel" type="button" value="{{ 'General_Cancel'|translate }}"/>
@@ -55,11 +58,16 @@
<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 class="row">
+ <div class="col s12 m6"><label for="copyDashboardName">{{ 'Dashboard_DashboardName'|translate }} </label></div>
+ <div class="col s12 m6"><input type="text" name="copyDashboardName" id="copyDashboardName" value=""/></div>
+ </div>
+ <div class="row">
+ <div class="col s12 m6"><label for="copyDashboardUser">{{ 'General_Username'|translate }} </label></div>
+ <div class="col s12 m6"><select class="browser-default" name="copyDashboardUser" id="copyDashboardUser"></select></div>
+ </div>
</div>
+
<input role="yes" type="button" value="{{ 'General_Ok'|translate }}"/>
<input role="cancel" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
@@ -69,20 +77,21 @@
<h2>{{ 'Dashboard_CreateNewDashboard'|translate }}</h2>
<div id="createDashboardNameInput">
- <label>{{ 'Dashboard_DashboardName'|translate }} <input type="input" name="newDashboardName" id="createDashboardName" value=""/></label><br/>
-
- <label for="dashboard_type_default">
+ <p>
+ <label>{{ 'Dashboard_DashboardName'|translate }} </label>
+ <input type="text" name="newDashboardName" id="createDashboardName" value=""/>
+ </p>
+ <p>
<input type="radio" checked="checked" name="type" value="default" id="dashboard_type_default" />
- {{ 'Dashboard_DefaultDashboard'|translate }}
- </label>
- <br/><br/>
-
- <label for="dashboard_type_empty">
+ <label for="dashboard_type_default">{{ 'Dashboard_DefaultDashboard'|translate }}</label>
+ </p>
+ <p>
<input type="radio" name="type" value="empty" id="dashboard_type_empty" />
- {{ 'Dashboard_EmptyDashboard'|translate }}
- </label>
+ <label for="dashboard_type_empty">{{ 'Dashboard_EmptyDashboard'|translate }}</label>
+ </p>
+
</div>
- <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
+ <input role="yes" type="button" value="{{ 'General_Ok'|translate }}"/>
<input role="no" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
@@ -91,10 +100,10 @@
<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="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
{% include "@Dashboard/_widgetFactoryTemplate.twig" %}
- <div id="dashboardWidgetsArea"></div>
+ <div id="dashboardWidgetsArea" class="row"></div>
</div> \ No newline at end of file
diff --git a/plugins/DevicesDetection/templates/detection.twig b/plugins/DevicesDetection/templates/detection.twig
index 7a0b524064..f5d5387b6f 100644
--- a/plugins/DevicesDetection/templates/detection.twig
+++ b/plugins/DevicesDetection/templates/detection.twig
@@ -15,13 +15,11 @@
}, 'GET');
ajaxHandler.setFormat('html');
ajaxHandler.setCallback(function(response){
- $('.itemList').html(response).dialog({
- modal: true,
- width: '50%',
- maxHeight: 400
- });
+ var $list = $('#deviceDetectionItemList');
+ $list.find('.itemList').html(response);
+ piwikHelper.modalConfirm($list, [], {fixedFooter: true});
});
- ajaxHandler.send(true);
+ ajaxHandler.send();
}
</script>
@@ -43,65 +41,73 @@
}
</style>
- <h2>{{ title }}</h2>
+ <div piwik-content-block content-title="{{ title|e('html_attr') }}">
+ <h3>{{ 'DevicesDetection_UserAgent'|translate|e('html_attr') }}</h3>
- <h3>{{ 'DevicesDetection_UserAgent'|translate }}</h3>
+ <form action="{{ linkTo({}) }}" method="POST">
+ <textarea name="ua">{{ userAgent }}</textarea>
+ <br />
+ <input type="submit" value="{{ 'General_Refresh'|translate }}" />
+ </form>
- <br />
- <form action="{{ linkTo({}) }}" method="POST">
- <textarea name="ua">{{ userAgent }}</textarea>
- <br />
- <input type="submit" value="{{ 'General_Refresh'|translate }}" />
- </form>
+ <h3>{{ 'DevicesDetection_ColumnOperatingSystem'|translate|e('html_attr') }}</h3>
+ <table class="detection" piwik-content-table>
+ <tbody>
+ <tr>
+ <td>{{ 'General_Name'|translate }} <small>(<a href="javascript:showList('os');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
+ <td><img src="{{ os_logo }}" />{{ os_name }}</td>
+ </tr>
+ <tr>
+ <td>{{ 'CorePluginsAdmin_Version'|translate }}</td>
+ <td>{{ os_version }}</td>
+ </tr>
+ <tr>
+ <td>{{ 'DevicesDetection_OperatingSystemFamily'|translate }} <small>(<a href="javascript:showList('osfamilies');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
+ <td><img src="{{ os_family_logo }}" />{{ os_family }}</td>
+ </tr>
+ </tbody>
+ </table>
- <h3>{{ 'DevicesDetection_ColumnOperatingSystem'|translate }}</h3>
- <table class="simple-table detection">
- <tr>
- <td>{{ 'General_Name'|translate }} <small>(<a href="javascript:showList('os');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
- <td><img src="{{ os_logo }}" />{{ os_name }}</td>
- </tr>
- <tr>
- <td>{{ 'CorePluginsAdmin_Version'|translate }}</td>
- <td>{{ os_version }}</td>
- </tr>
- <tr>
- <td>{{ 'DevicesDetection_OperatingSystemFamily'|translate }} <small>(<a href="javascript:showList('osfamilies');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
- <td><img src="{{ os_family_logo }}" />{{ os_family }}</td>
- </tr>
- </table>
+ <h3>{{ 'DevicesDetection_ColumnBrowser'|translate }}</h3>
+ <table class="detection" piwik-content-table>
+ <tbody>
+ <tr>
+ <td>{{ 'General_Name'|translate }} <small>(<a href="javascript:showList('browsers');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
+ <td><img src="{{ browser_logo }}" />{{ browser_name }}</td>
+ </tr>
+ <tr>
+ <td>{{ 'CorePluginsAdmin_Version'|translate }}</td>
+ <td>{{ browser_version }}</td>
+ </tr>
+ <tr>
+ <td>{{ 'DevicesDetection_BrowserFamily'|translate }} <small>(<a href="javascript:showList('browserfamilies');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
+ <td><img src="{{ browser_family_logo }}" />{{ browser_family }}</td>
+ </tr>
+ </tbody>
+ </table>
- <h3>{{ 'DevicesDetection_ColumnBrowser'|translate }}</h3>
- <table class="simple-table detection">
- <tr>
- <td>{{ 'General_Name'|translate }} <small>(<a href="javascript:showList('browsers');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
- <td><img src="{{ browser_logo }}" />{{ browser_name }}</td>
- </tr>
- <tr>
- <td>{{ 'CorePluginsAdmin_Version'|translate }}</td>
- <td>{{ browser_version }}</td>
- </tr>
- <tr>
- <td>{{ 'DevicesDetection_BrowserFamily'|translate }} <small>(<a href="javascript:showList('browserfamilies');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
- <td><img src="{{ browser_family_logo }}" />{{ browser_family }}</td>
- </tr>
- </table>
+ <h3>{{ 'DevicesDetection_Device'|translate }}</h3>
+ <table class="detection" piwik-content-table>
+ <tbody>
+ <tr>
+ <td>{{ 'DevicesDetection_dataTableLabelTypes'|translate }} <small>(<a href="javascript:showList('devicetypes');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
+ <td><img src="{{ device_type_logo }}" />{{ device_type }}</td>
+ </tr>
+ <tr>
+ <td>{{ 'DevicesDetection_dataTableLabelBrands'|translate }} <small>(<a href="javascript:showList('brands');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
+ <td><img src="{{ device_brand_logo }}" />{{ device_brand }}</td>
+ </tr>
+ <tr>
+ <td>{{ 'DevicesDetection_dataTableLabelModels'|translate }}</td>
+ <td>{{ device_model }}</td>
+ </tr></tbody>
+ </table>
- <h3>{{ 'DevicesDetection_Device'|translate }}</h3>
- <table class="simple-table detection">
- <tr>
- <td>{{ 'DevicesDetection_dataTableLabelTypes'|translate }} <small>(<a href="javascript:showList('devicetypes');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
- <td><img src="{{ device_type_logo }}" />{{ device_type }}</td>
- </tr>
- <tr>
- <td>{{ 'DevicesDetection_dataTableLabelBrands'|translate }} <small>(<a href="javascript:showList('brands');">{{ 'Mobile_ShowAll'|translate }}</a>)</small></td>
- <td><img src="{{ device_brand_logo }}" />{{ device_brand }}</td>
- </tr>
- <tr>
- <td>{{ 'DevicesDetection_dataTableLabelModels'|translate }}</td>
- <td>{{ device_model }}</td>
- </tr>
- </table>
+ </div>
- <div style="display: none;" class="itemList"></div>
+ <div class="ui-confirm" id="deviceDetectionItemList">
+ <div class="itemList"> </div>
+ <input role="close" type="button" value="{{ 'General_Close'|translate }}"/>
+ </div>
{% endblock %}
diff --git a/plugins/DevicesDetection/templates/list.twig b/plugins/DevicesDetection/templates/list.twig
index 1fa57d844d..d51fb732ff 100644
--- a/plugins/DevicesDetection/templates/list.twig
+++ b/plugins/DevicesDetection/templates/list.twig
@@ -1,4 +1,4 @@
-<table class="dataTable">
+<table class="entityTable">
{% for name,image in itemList %}
<tr>
<td><img src="{{ image }}" /> {{ name }}</td>
diff --git a/plugins/Diagnostics/Diagnostic/GdExtensionCheck.php b/plugins/Diagnostics/Diagnostic/GdExtensionCheck.php
index 3d79eb3bdc..328d3c5317 100644
--- a/plugins/Diagnostics/Diagnostic/GdExtensionCheck.php
+++ b/plugins/Diagnostics/Diagnostic/GdExtensionCheck.php
@@ -40,6 +40,6 @@ class GdExtensionCheck implements Diagnostic
$this->translator->translate('Installation_SystemCheckGDHelp')
);
- return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment));
+ return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_ERROR, $comment));
}
}
diff --git a/plugins/Diagnostics/stylesheets/configfile.less b/plugins/Diagnostics/stylesheets/configfile.less
index f399ce3596..16d4110b5a 100644
--- a/plugins/Diagnostics/stylesheets/configfile.less
+++ b/plugins/Diagnostics/stylesheets/configfile.less
@@ -19,4 +19,9 @@
width: 25%;
}
+ td.description {
+ word-wrap: break-word;
+ word-break: break-word;
+ }
+
}
diff --git a/plugins/Diagnostics/templates/configfile.twig b/plugins/Diagnostics/templates/configfile.twig
index 46113b51da..a50337fb49 100644
--- a/plugins/Diagnostics/templates/configfile.twig
+++ b/plugins/Diagnostics/templates/configfile.twig
@@ -14,7 +14,8 @@
{% endmacro %}
{% block content %}
- <h2 piwik-enriched-headline>{{ 'Diagnostics_ConfigFileTitle'|translate }}</h2>
+<div piwik-content-block
+ content-title="{{ 'Diagnostics_ConfigFileTitle'|translate|e('html_attr') }}" feature="true">
<p>
{{ 'Diagnostics_ConfigFileIntroduction'|translate('<code>"config/config.ini.php"</code>')|raw }}
{{ 'Diagnostics_HideUnchanged'|translate('<a ng-click="hideGlobalConfigValues=!hideGlobalConfigValues">', '</a>')|raw }}
@@ -25,7 +26,7 @@
{% endfor %}
</p>
- <table class="simple-table diagnostics configfile">
+ <table class="diagnostics configfile" piwik-content-table>
<tbody>
{% for category, configValues in allConfigValues %}
<tr><td colspan="3"><a name="{{ category|e('html_attr') }}"></a><h3>{{ category }}</h3></td></tr>
@@ -52,4 +53,5 @@
</tbody>
</table>
+</div>
{% endblock %} \ No newline at end of file
diff --git a/plugins/Ecommerce/Controller.php b/plugins/Ecommerce/Controller.php
index 16d324ab0d..c1716a5808 100644
--- a/plugins/Ecommerce/Controller.php
+++ b/plugins/Ecommerce/Controller.php
@@ -13,6 +13,7 @@ use Piwik\Common;
use Piwik\DataTable;
use Piwik\FrontController;
use Piwik\Piwik;
+use Piwik\Plugins\Goals\API as GoalsApi;
use Piwik\Translation\Translator;
use Piwik\View;
use Piwik\Plugins\Goals\TranslationHelper;
@@ -43,10 +44,11 @@ class Controller extends \Piwik\Plugins\Goals\Controller
$goalDefinition['name'] = $this->translator->translate('Goals_Ecommerce');
$goalDefinition['allow_multiple'] = true;
} else {
- if (!isset($this->goals[$idGoal])) {
+ $goals = GoalsApi::getInstance()->getGoals($this->idSite);
+ if (!isset($goals[$idGoal])) {
Piwik::redirectToModule('Goals', 'index', array('idGoal' => null));
}
- $goalDefinition = $this->goals[$idGoal];
+ $goalDefinition = $goals[$idGoal];
}
$this->setGeneralVariablesView($view);
diff --git a/plugins/Ecommerce/Widgets/GetEcommerceLog.php b/plugins/Ecommerce/Widgets/GetEcommerceLog.php
index d1a68a435a..8780a0578b 100644
--- a/plugins/Ecommerce/Widgets/GetEcommerceLog.php
+++ b/plugins/Ecommerce/Widgets/GetEcommerceLog.php
@@ -20,8 +20,13 @@ class GetEcommerceLog extends \Piwik\Widget\Widget
$config->setSubcategoryId('Goals_EcommerceLog');
$config->setName('Goals_EcommerceLog');
- $idSite = Common::getRequestVar('idSite', null, 'int');
- $site = new Site($idSite);
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
+ if (empty($idSite)) {
+ $config->disable();
+ return;
+ }
+
+ $site = new Site($idSite);
$config->setIsEnabled($site->isEcommerceEnabled());
}
diff --git a/plugins/Ecommerce/templates/conversionOverview.twig b/plugins/Ecommerce/templates/conversionOverview.twig
index 1da5c3a926..9578987353 100644
--- a/plugins/Ecommerce/templates/conversionOverview.twig
+++ b/plugins/Ecommerce/templates/conversionOverview.twig
@@ -1,19 +1,22 @@
-<ul class="ulGoalTopElements">
- {{ 'General_ColumnRevenue'|translate }}: {{ revenue|money(idSite)|raw -}}
- {% if revenue_subtotal is not empty %},
- {{ 'General_Subtotal'|translate }}: {{ revenue_subtotal|money(idSite)|raw -}}
- {% endif %}
- {%- if revenue_tax is not empty -%},
- {{ 'General_Tax'|translate }}: {{ revenue_tax|money(idSite)|raw -}}
- {% endif %}
- {%- if revenue_shipping is not empty -%},
- {{ 'General_Shipping'|translate }}: {{ revenue_shipping|money(idSite)|raw -}}
- {% endif %}
- {%- if revenue_discount is not empty -%},
- {{ 'General_Discount'|translate }}: {{ revenue_discount|money(idSite)|raw -}}
- {% endif %}
-</ul>
-<a href="javascript:;" class="segmentedlog" onclick="SegmentedVisitorLog.show('Goals.getMetrics', 'visitConvertedGoalId=={{ idGoal }}', {})">
- {{ 'Live_RowActionTooltipWithDimension'|translate('General_Goal'|translate) }}
-</a>
-<br style="clear:left"/> \ No newline at end of file
+<div piwik-content-block
+ content-title="{{ 'Goals_ConversionsOverview'|translate|e('html_attr') }}">
+ <ul class="ulGoalTopElements">
+ {{ 'General_ColumnRevenue'|translate }}: {{ revenue|money(idSite)|raw -}}
+ {% if revenue_subtotal is not empty %},
+ {{ 'General_Subtotal'|translate }}: {{ revenue_subtotal|money(idSite)|raw -}}
+ {% endif %}
+ {%- if revenue_tax is not empty -%},
+ {{ 'General_Tax'|translate }}: {{ revenue_tax|money(idSite)|raw -}}
+ {% endif %}
+ {%- if revenue_shipping is not empty -%},
+ {{ 'General_Shipping'|translate }}: {{ revenue_shipping|money(idSite)|raw -}}
+ {% endif %}
+ {%- if revenue_discount is not empty -%},
+ {{ 'General_Discount'|translate }}: {{ revenue_discount|money(idSite)|raw -}}
+ {% endif %}
+ </ul>
+ <a href="javascript:;" class="segmentedlog" onclick="SegmentedVisitorLog.show('Goals.getMetrics', 'visitConvertedGoalId=={{ idGoal }}', {})">
+ {{ 'Live_RowActionTooltipWithDimension'|translate('General_Goal'|translate) }}
+ </a>
+ <br style="clear:left"/>
+</div> \ No newline at end of file
diff --git a/plugins/Ecommerce/templates/getSparklines.twig b/plugins/Ecommerce/templates/getSparklines.twig
index 5e56ad868f..62487c46e2 100644
--- a/plugins/Ecommerce/templates/getSparklines.twig
+++ b/plugins/Ecommerce/templates/getSparklines.twig
@@ -1,3 +1,4 @@
+<div class="card"><div class="card-content">
<div id='leftcolumn' style="clear:both;{% if not isWidget %}width:33%;'{% endif %}">
<div class="sparkline">{{ sparkline(urlSparklineConversions) }}
<strong>{{ nb_conversions|number }}</strong>
@@ -30,7 +31,7 @@
</div>
<div id='rightcolumn' {% if not isWidget %}style='width:30%;'{% endif %}>
<div>
- <img src='plugins/Morpheus/images/ecommerceAbandonedCart.gif'> <em>{{ 'General_AbandonedCarts'|translate }}</em>
+ <img src='plugins/Morpheus/images/ecommerceAbandonedCart.gif'> {{ 'General_AbandonedCarts'|translate }}
</div>
<div class="sparkline">
@@ -54,3 +55,4 @@
</div>
<div style="clear: left;"></div>
{% include "_sparklineFooter.twig" %}
+ </div></div> \ No newline at end of file
diff --git a/plugins/ExamplePlugin/Widgets/MyExampleWidget.php b/plugins/ExamplePlugin/Widgets/MyExampleWidget.php
index 3434a3aac9..2ca69752d8 100644
--- a/plugins/ExamplePlugin/Widgets/MyExampleWidget.php
+++ b/plugins/ExamplePlugin/Widgets/MyExampleWidget.php
@@ -71,10 +71,9 @@ class MyExampleWidget extends Widget
*/
public function render()
{
- // $view = new View('@ExamplePlugin/myViewTemplate');
- // return $view->render();
+ // or: return $this->renderTemplate('myViewTemplate', array(...view variables...));
- return '<div>My Widget Text</div>';
+ return '<div class="widgetBody">My Widget Text</div>';
}
} \ No newline at end of file
diff --git a/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePage.png b/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePage.png
index b235232595..32cf4f5fd6 100644
--- a/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePage.png
+++ b/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePage.png
Binary files differ
diff --git a/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePagePartial.png b/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePagePartial.png
index e32976c66e..3362769509 100644
--- a/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePagePartial.png
+++ b/plugins/ExamplePlugin/tests/UI/expected-ui-screenshots/SimpleUITest_simplePagePartial.png
Binary files differ
diff --git a/plugins/ExampleRssWidget/stylesheets/rss.less b/plugins/ExampleRssWidget/stylesheets/rss.less
index 5676571a7e..11f4d9dbbe 100644
--- a/plugins/ExampleRssWidget/stylesheets/rss.less
+++ b/plugins/ExampleRssWidget/stylesheets/rss.less
@@ -28,6 +28,6 @@
.rss-content, .rss-description {
clear: both;
line-height: 1.5em;
- font-size: 11px;
+ font-size: 13px;
color: #333333;
}
diff --git a/plugins/ExampleSettingsPlugin/SystemSettings.php b/plugins/ExampleSettingsPlugin/SystemSettings.php
index 1d2082e949..c65763ee6d 100644
--- a/plugins/ExampleSettingsPlugin/SystemSettings.php
+++ b/plugins/ExampleSettingsPlugin/SystemSettings.php
@@ -54,7 +54,6 @@ class SystemSettings extends \Piwik\Settings\Plugin\SystemSettings
$field->title = 'Metric to display';
$field->uiControl = FieldConfig::UI_CONTROL_SINGLE_SELECT;
$field->availableValues = array('nb_visits' => 'Visits', 'nb_actions' => 'Actions', 'visitors' => 'Visitors');
- $field->introduction = 'Only Super Users can change the following settings:';
$field->description = 'Choose the metric that should be displayed in the browser tab';
});
}
diff --git a/plugins/ExampleTheme/stylesheets/theme.less b/plugins/ExampleTheme/stylesheets/theme.less
index 6b68780734..ee7491395e 100644
--- a/plugins/ExampleTheme/stylesheets/theme.less
+++ b/plugins/ExampleTheme/stylesheets/theme.less
@@ -1,7 +1,16 @@
-@theme-fontFamily-base: Verdana, sans-serif;
+@theme-fontFamily-base: Arial, Verdana, sans-serif;
@theme-color-brand: #5793d4;
@theme-color-background-base: #d9e0e3;
+@theme-color-header-background: #0091ea;
+
+@theme-color-widget-title-background: #80d8ff;
+@theme-color-widget-title-text: #01579b;
+
+@theme-color-menu-contrast-text: #0091ea;
+@theme-color-menu-contrast-textActive: #006064;
+@theme-color-menu-contrast-textSelected: #00838f;
+@theme-color-menu-contrast-background: #e1f5fe;
/*
@theme-color-brand: #d4291f;
diff --git a/plugins/Feedback/angularjs/ratefeature/ratefeature.directive.html b/plugins/Feedback/angularjs/ratefeature/ratefeature.directive.html
index 8961b43c64..8c5946120a 100644
--- a/plugins/Feedback/angularjs/ratefeature/ratefeature.directive.html
+++ b/plugins/Feedback/angularjs/ratefeature/ratefeature.directive.html
@@ -27,6 +27,7 @@
<input type="button"
title="{{ 'Feedback_RateFeatureSendFeedbackInformation'|translate }}"
value="{{ 'Feedback_SendFeedback'|translate }}" role="yes"/>
+ <input type="button" role="cancel" value="{{ 'General_Cancel'|translate }}"/>
</div>
<div class="ui-confirm ratefeatureDialog" piwik-dialog="rateFeature.ratingDone" yes="">
diff --git a/plugins/Feedback/stylesheets/feedback.less b/plugins/Feedback/stylesheets/feedback.less
index 80b98101cc..9f33886fca 100644
--- a/plugins/Feedback/stylesheets/feedback.less
+++ b/plugins/Feedback/stylesheets/feedback.less
@@ -1,4 +1,6 @@
#feedback-faq {
+ max-width: 850px;
+ margin: 0 auto;
ul {
list-style: none;
@@ -7,19 +9,11 @@
line-height: 18px;
}
- .header_full, p {
- padding-bottom: 0px;
- }
-
p {
line-height: 1.7em;
font-size: 13px;
}
- div {
- max-width: 850px;
- }
-
.piwik-donate-call {
border: 0px;
padding-left: 0px;
@@ -43,9 +37,10 @@
.footer {
text-align: center;
+ margin-top: 48px;
a {
- color: black;
+ color: @theme-color-text-contrast;
&:hover {
text-decoration: underline;
}
@@ -62,7 +57,7 @@
.claim {
font-size: 13px;
line-height: 16px;
- color: #808080;
+ color: @theme-color-text-contrast;
margin-bottom: 50px;
margin-top: 15px;
}
diff --git a/plugins/Feedback/templates/index.twig b/plugins/Feedback/templates/index.twig
index 214c883647..04d3811e7d 100644
--- a/plugins/Feedback/templates/index.twig
+++ b/plugins/Feedback/templates/index.twig
@@ -8,27 +8,23 @@
{% block content %}
<div id="feedback-faq" class="admin">
- <h2 piwik-enriched-headline
- feature-name="{{ 'General_Help'|translate }}"
- >{{ headline }}</h2>
-
- <div class="header_full">
+ <div piwik-content-block
+ content-title="{{ headline|e('html_attr') }}"
+ feature="{{ 'General_Help'|translate|e('html_attr') }}">
<p>{{ 'General_PiwikIsACollaborativeProjectYouCanContributeAndDonate'|translate(
- "<a href='?module=Proxy&action=redirect&url=http://piwik.org' target='_blank'>",
- "</a>",
- "<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/get-involved/'>",
- "</a>",
- "<a href='#donate'>",
- "</a>",
- "<a href='?module=Proxy&action=redirect&url=http://piwik.org/team/' target='_blank'>",
- "</a>"
- )|raw }}
+ "<a href='?module=Proxy&action=redirect&url=http://piwik.org' target='_blank'>",
+ "</a>",
+ "<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/get-involved/'>",
+ "</a>",
+ "<a href='#donate'>",
+ "</a>",
+ "<a href='?module=Proxy&action=redirect&url=http://piwik.org/team/' target='_blank'>",
+ "</a>"
+ )|raw }}
</p>
</div>
- <h2>{{ 'Feedback_CommunityHelp'|translate }}</h2>
-
- <div class="header_full">
+ <div piwik-content-block content-title="{{ 'Feedback_CommunityHelp'|translate|e('html_attr') }}">
<p> &bull; {{ 'Feedback_ViewUserGuides'|translate("<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/'>","</a>")|raw }}.</p>
<p> &bull; {{ 'Feedback_ViewAnswersToFAQ'|translate("<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/faq/'>","</a>")|raw }}.</p>
<p> &bull; {{ 'Feedback_VisitTheForums'|translate("<a target='_blank' href='?module=Proxy&action=redirect&url=http://forum.piwik.org/'>","</a>")|raw }}.</p>
@@ -38,9 +34,7 @@
}} ({{ 'API_Glossary'|translate }})</p>
</div>
- <h2>{{ 'Feedback_ProfessionalHelp'|translate }}</h2>
-
- <div class="header_full">
+ <div piwik-content-block content-title="{{ 'Feedback_ProfessionalHelp'|translate|e('html_attr') }}">
<p>{{ 'Feedback_PiwikProIntro'|translate }}</p>
<p>{{ 'Feedback_PiwikProOfferIntro'|translate }}:</p>
@@ -51,33 +45,30 @@
<p> &bull; {{ 'Feedback_PiwikProPremiumFeatures'|translate }}</p>
<p> &bull; {{ 'Feedback_PiwikProCustomDevelopment'|translate }}</p>
<p> &bull; {{ 'Feedback_PiwikProAnalystConsulting'|translate }}</p>
- </div>
-
- <form target="_blank" action="https://piwik.pro/contact#contact-form">
- <input type="hidden" name="pk_campaign" value="App_Help">
- <input type="hidden" name="pk_source" value="Piwik_App">
- <input type="hidden" name="pk_medium" value="App_ContactUs_button">
- <br />
- <input type="submit" value="{{ 'Feedback_ContactUs'|translate }}">
- </form>
- <h2>{{ 'Feedback_DoYouHaveBugReportOrFeatureRequest'|translate }}</h2>
+ <form target="_blank" action="https://piwik.pro/contact#contact-form">
+ <input type="hidden" name="pk_campaign" value="App_Help">
+ <input type="hidden" name="pk_source" value="Piwik_App">
+ <input type="hidden" name="pk_medium" value="App_ContactUs_button">
+ <br />
+ <input type="submit" class="btn" value="{{ 'Feedback_ContactUs'|translate }}">
+ </form>
+ </div>
- <div class="header_full">
- <p>{{ 'Feedback_HowToCreateTicket'|translate(
- "<a target='_blank' href='?module=Proxy&action=redirect&url=http://developer.piwik.org/guides/core-team-workflow%23submitting-a-bug-report'>",
- "</a>",
- "<a target='_blank' href='?module=Proxy&action=redirect&url=http://developer.piwik.org/guides/core-team-workflow%23submitting-a-feature-request'>",
- "</a>",
- "<a target='_blank' href='?module=Proxy&action=redirect&url=https://github.com/piwik/piwik/issues'>",
- "</a>",
- "<a target='_blank' href='?module=Proxy&action=redirect&url=https://github.com/piwik/piwik/issues/new'>",
- "</a>"
- )|raw }}</p>
+ <div piwik-content-block content-title="{{ 'Feedback_DoYouHaveBugReportOrFeatureRequest'|translate|e('html_attr') }}">
+ <p>{{ 'Feedback_HowToCreateTicket'|translate(
+ "<a target='_blank' href='?module=Proxy&action=redirect&url=http://developer.piwik.org/guides/core-team-workflow%23submitting-a-bug-report'>",
+ "</a>",
+ "<a target='_blank' href='?module=Proxy&action=redirect&url=http://developer.piwik.org/guides/core-team-workflow%23submitting-a-feature-request'>",
+ "</a>",
+ "<a target='_blank' href='?module=Proxy&action=redirect&url=https://github.com/piwik/piwik/issues'>",
+ "</a>",
+ "<a target='_blank' href='?module=Proxy&action=redirect&url=https://github.com/piwik/piwik/issues/new'>",
+ "</a>"
+ )|raw }}</p>
</div>
<div class="footer">
- <hr/>
<ul class="social">
<li>
<a target="_blank" href="?module=Proxy&action=redirect&url=http://piwik.org/newsletter/"><img class="icon" src="plugins/Feedback/images/newsletter.png"></a>
diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php
index 5d7a5168e2..3f649b9399 100644
--- a/plugins/Goals/Controller.php
+++ b/plugins/Goals/Controller.php
@@ -47,6 +47,7 @@ class Controller extends \Piwik\Plugin\Controller
* @var Translator
*/
private $translator;
+ private $goals;
private function formatConversionRate($conversionRate, $columnName = 'conversion_rate')
{
@@ -82,7 +83,7 @@ class Controller extends \Piwik\Plugin\Controller
$view = new View('@Goals/manageGoals');
$this->setGeneralVariablesView($view);
$this->setEditGoalsViewVariables($view);
- $this->setUserCanEditGoals($view);
+ $this->setGoalOptions($view);
return $view->render();
}
@@ -125,7 +126,7 @@ class Controller extends \Piwik\Plugin\Controller
{
$view = new View('@Goals/addNewGoal');
$this->setGeneralVariablesView($view);
- $this->setUserCanEditGoals($view);
+ $this->setGoalOptions($view);
$view->onlyShowAddNewGoal = true;
return $view->render();
}
@@ -135,7 +136,7 @@ class Controller extends \Piwik\Plugin\Controller
$view = new View('@Goals/editGoals');
$this->setGeneralVariablesView($view);
$this->setEditGoalsViewVariables($view);
- $this->setUserCanEditGoals($view);
+ $this->setGoalOptions($view);
return $view->render();
}
@@ -346,9 +347,34 @@ class Controller extends \Piwik\Plugin\Controller
$view->ecommerceEnabled = $this->site->isEcommerceEnabled();
}
- private function setUserCanEditGoals(View $view)
+ private function setGoalOptions(View $view)
{
$view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
+ $view->goalTriggerTypeOptions = array(
+ 'visitors' => Piwik::translate('Goals_WhenVisitors'),
+ 'manually' => Piwik::translate('Goals_Manually')
+ );
+ $view->goalMatchAttributeOptions = array(
+ array('key' => 'url', 'value' => Piwik::translate('Goals_VisitUrl')),
+ array('key' => 'title', 'value' => Piwik::translate('Goals_VisitPageTitle')),
+ array('key' => 'event', 'value' => Piwik::translate('Goals_SendEvent')),
+ array('key' => 'file', 'value' => Piwik::translate('Goals_Download')),
+ array('key' => 'external_website', 'value' => Piwik::translate('Goals_ClickOutlink')),
+ );
+ $view->allowMultipleOptions = array(
+ array('key' => '0', 'value' => Piwik::translate('Goals_DefaultGoalConvertedOncePerVisit')),
+ array('key' => '1', 'value' => Piwik::translate('Goals_AllowGoalConvertedMoreThanOncePerVisit'))
+ );
+ $view->eventTypeOptions = array(
+ array('key' => 'event_category', 'value' => Piwik::translate('Events_EventCategory')),
+ array('key' => 'event_action', 'value' => Piwik::translate('Events_EventAction')),
+ array('key' => 'event_name', 'value' => Piwik::translate('Events_EventName'))
+ );
+ $view->patternTypeOptions = array(
+ array('key' => 'contains', 'value' => Piwik::translate('Goals_Contains', '')),
+ array('key' => 'exact', 'value' => Piwik::translate('Goals_IsExactly', '')),
+ array('key' => 'regex', 'value' => Piwik::translate('Goals_MatchesExpression', ''))
+ );
}
/**
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index ae9ea61cda..b478be5b16 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -259,7 +259,8 @@ class Goals extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
- $jsFiles[] = "plugins/Goals/javascripts/goalsForm.js";
+ $jsFiles[] = "plugins/Goals/angularjs/manage-goals/manage-goals.controller.js";
+ $jsFiles[] = "plugins/Goals/angularjs/manage-goals/manage-goals.directive.js";
}
public function getStylesheetFiles(&$stylesheets)
@@ -276,6 +277,7 @@ class Goals extends \Piwik\Plugin
public function getClientSideTranslationKeys(&$translationKeys)
{
$translationKeys[] = 'Goals_AddGoal';
+ $translationKeys[] = 'Goals_AddNewGoal';
$translationKeys[] = 'Goals_UpdateGoal';
$translationKeys[] = 'Goals_DeleteGoalConfirm';
$translationKeys[] = 'Goals_UpdateGoal';
diff --git a/plugins/Goals/Menu.php b/plugins/Goals/Menu.php
index 542eba461b..a1c6b26449 100644
--- a/plugins/Goals/Menu.php
+++ b/plugins/Goals/Menu.php
@@ -21,7 +21,7 @@ class Menu extends \Piwik\Plugin\Menu
$idSite = $this->getIdSite($userPreferences->getDefaultWebsiteId());
if (Piwik::isUserHasAdminAccess($idSite)) {
- $menu->addManageItem('Goals_Goals', $this->urlForAction('manage', array('idSite' => $idSite)), 40);
+ $menu->addMeasurableItem('Goals_Goals', $this->urlForAction('manage', array('idSite' => $idSite)), 40);
}
}
diff --git a/plugins/Goals/Pages.php b/plugins/Goals/Pages.php
index be55bff144..3399aa19c3 100644
--- a/plugins/Goals/Pages.php
+++ b/plugins/Goals/Pages.php
@@ -303,7 +303,7 @@ class Pages
$widget->setIsNotWidgetizable();
if (!empty($report['viewDataTable'])) {
- $widget->setDefaultViewDataTable($report['viewDataTable']);
+ $widget->forceViewDataTable($report['viewDataTable']);
}
$container->addWidgetConfig($widget);
diff --git a/plugins/Goals/Reports/Get.php b/plugins/Goals/Reports/Get.php
index d7180547f0..b6842b1722 100644
--- a/plugins/Goals/Reports/Get.php
+++ b/plugins/Goals/Reports/Get.php
@@ -40,10 +40,32 @@ class Get extends Base
$this->parameters = null;
}
+ private function getGoals()
+ {
+ $idSite = $this->getIdSite();
+ $goals = API::getInstance()->getGoals($idSite);
+ return $goals;
+ }
+
+ private function getGoal($goalId)
+ {
+ $goals = $this->getGoals();
+
+ if (!empty($goals[$goalId])) {
+
+ return $goals[$goalId];
+ }
+ }
+
public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
{
- $idSite = $this->getIdSite();
- $goals = API::getInstance()->getGoals($idSite);
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
+
+ if (empty($idSite)) {
+ return;
+ }
+
+ $goals = $this->getGoals();
$reports = Goals::getReportsWithGoalMetrics();
$page = new Pages($factory, $reports);
@@ -77,12 +99,24 @@ class Get extends Base
public function configureView(ViewDataTable $view)
{
+ $idGoal = Common::getRequestVar('idGoal', 0, 'string');
+
if ($view->isViewDataTableId(Sparklines::ID)) {
/** @var Sparklines $view */
$idSite = $this->getIdSite();
$isEcommerceEnabled = $this->isEcommerceEnabled($idSite);
- $idGoal = Common::getRequestVar('idGoal', 0, 'int');
+ $onlySummary = Common::getRequestVar('only_summary', 0, 'int');
+
+ if ($onlySummary && !empty($idGoal)) {
+ // in Goals overview summary we show proper title for a goal
+ $goal = $this->getGoal($idGoal);
+ if (!empty($goal['name'])) {
+ $view->config->title = Piwik::translate('Goals_GoalX', "'" . $goal['name'] . "'");
+ }
+ } else {
+ $view->config->title = '';
+ }
$numberFormatter = NumberFormatter::getInstance();
$view->config->filters[] = function (DataTable $table) use ($numberFormatter, $idSite) {
@@ -138,8 +172,6 @@ class Get extends Base
}
} else {
- $onlySummary = Common::getRequestVar('only_summary', 0, 'int');
-
if ($onlySummary) {
// in Goals Overview we list an overview for each goal....
$view->config->addTranslation('conversion_rate', Piwik::translate('Goals_ConversionRate'));
@@ -150,6 +182,13 @@ class Get extends Base
}
}
} else if ($view->isViewDataTableId(Evolution::ID)) {
+ $goal = $this->getGoal($idGoal);
+ if (!empty($goal['name'])) {
+ $view->config->title = Piwik::translate('Goals_GoalX', "'" . $goal['name'] . "'");
+ } else {
+ $view->config->title = Piwik::translate('General_EvolutionOverPeriod');
+ }
+
if (empty($view->config->columns_to_display)) {
$view->config->columns_to_display = array('nb_conversions');
}
diff --git a/plugins/Goals/Visualizations/Goals.php b/plugins/Goals/Visualizations/Goals.php
index 5619e403a6..4be5e24ef0 100644
--- a/plugins/Goals/Visualizations/Goals.php
+++ b/plugins/Goals/Visualizations/Goals.php
@@ -22,7 +22,7 @@ use Piwik\View;
class Goals extends HtmlTable
{
const ID = 'tableGoals';
- const FOOTER_ICON = 'plugins/Morpheus/images/goal.png';
+ const FOOTER_ICON = 'icon-goal';
const FOOTER_ICON_TITLE = 'General_DisplayTableWithGoalMetrics';
public function beforeLoadDataTable()
diff --git a/plugins/Goals/Widgets/AddNewGoal.php b/plugins/Goals/Widgets/AddNewGoal.php
index b692309b9d..c1207cde37 100644
--- a/plugins/Goals/Widgets/AddNewGoal.php
+++ b/plugins/Goals/Widgets/AddNewGoal.php
@@ -17,14 +17,20 @@ class AddNewGoal extends \Piwik\Widget\Widget
{
public static function configure(WidgetConfig $config)
{
- $idSite = Common::getRequestVar('idSite', null, 'int');
- $goals = API::getInstance()->getGoals($idSite);
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
$config->setCategoryId('Goals_Goals');
$config->setSubcategoryId('Goals_AddNewGoal');
$config->setParameters(array('idGoal' => ''));
$config->setIsNotWidgetizable();
+ if (empty($idSite)) {
+ $config->disable();
+ return;
+ }
+
+ $goals = API::getInstance()->getGoals($idSite);
+
if (Piwik::isUserHasAdminAccess($idSite)) {
$config->setName('Goals_AddNewGoal');
} else {
diff --git a/plugins/Goals/Widgets/EditGoals.php b/plugins/Goals/Widgets/EditGoals.php
index d0bb52667c..69457a39c4 100644
--- a/plugins/Goals/Widgets/EditGoals.php
+++ b/plugins/Goals/Widgets/EditGoals.php
@@ -17,13 +17,19 @@ class EditGoals extends \Piwik\Widget\Widget
{
public static function configure(WidgetConfig $config)
{
- $idSite = Common::getRequestVar('idSite', null, 'int');
- $goals = API::getInstance()->getGoals($idSite);
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
$config->setCategoryId('Goals_Goals');
$config->setSubcategoryId('Goals_ManageGoals');
$config->setIsNotWidgetizable();
+ if (empty($idSite)) {
+ $config->disable();
+ return;
+ }
+
+ $goals = API::getInstance()->getGoals($idSite);
+
if (Piwik::isUserHasAdminAccess($idSite)) {
$config->setName('Goals_ManageGoals');
} else {
diff --git a/plugins/Goals/angularjs/manage-goals/manage-goals.controller.js b/plugins/Goals/angularjs/manage-goals/manage-goals.controller.js
new file mode 100644
index 0000000000..631d8c9afa
--- /dev/null
+++ b/plugins/Goals/angularjs/manage-goals/manage-goals.controller.js
@@ -0,0 +1,171 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ManageGoalsController', ManageGoalsController);
+
+ ManageGoalsController.$inject = ['piwik', 'piwikApi', '$timeout', '$location', 'reportingMenuModel'];
+
+ function ManageGoalsController(piwik, piwikApi, $timeout, $location, reportingMenuModel) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+
+ if (!this.goal) {
+ this.goal = {};
+ }
+ this.showEditGoal = false;
+ this.showGoalList = true;
+
+ function scrollToTop()
+ {
+ $timeout(function () {
+ piwik.helper.lazyScrollTo(".pageWrap", 200);
+ });
+ }
+
+ function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, pattern, patternType, caseSensitive, revenue, allowMultiple, goalId) {
+
+ self.goal = {};
+ self.goal.name = goalName;
+
+ if (matchAttribute == 'manually') {
+ self.goal.triggerType = 'manually';
+ matchAttribute = 'url';
+ } else {
+ self.goal.triggerType = 'visitors';
+ }
+
+ if (0 === matchAttribute.indexOf('event')) {
+ self.goal.eventType = matchAttribute;
+ matchAttribute = 'event';
+ } else {
+ self.goal.eventType = 'event_category';
+ }
+
+ self.goal.matchAttribute = matchAttribute;
+ self.goal.allowMultiple = allowMultiple;
+ self.goal.patternType = patternType;
+ self.goal.pattern = pattern;
+ self.goal.caseSensitive = caseSensitive;
+ self.goal.revenue = revenue;
+ self.goal.apiMethod = goalMethodAPI;
+
+ self.goal.submitText = submitText;
+ self.goal.goalId = goalId;
+
+ $timeout(function () {
+ var text = _pk_translate('Goals_AddNewGoal');
+ if (goalId) {
+ text = _pk_translate('Goals_UpdateGoal')
+ }
+
+ $('.addEditGoal .card-title').text(text);
+ });
+ }
+
+ this.isManuallyTriggered = function () {
+ return this.goal.triggerType == 'manually';
+ }
+
+ this.save = function () {
+
+ var parameters = {};
+ parameters.name = encodeURIComponent(this.goal.name);
+
+ if (this.isManuallyTriggered()) {
+ parameters.matchAttribute = 'manually';
+ parameters.patternType = 'regex';
+ parameters.pattern = '.*';
+ parameters.caseSensitive = 0;
+ } else {
+ parameters.matchAttribute = this.goal.matchAttribute;
+
+ if (parameters.matchAttribute === 'event') {
+ parameters.matchAttribute = this.goal.eventType;
+ }
+
+ parameters.patternType = this.goal.patternType;
+ parameters.pattern = encodeURIComponent(this.goal.pattern);
+ parameters.caseSensitive = this.goal.caseSensitive == true ? 1 : 0;
+ }
+ parameters.revenue = this.goal.revenue;
+ parameters.allowMultipleConversionsPerVisit = this.goal.allowMultiple == true ? 1 : 0;
+
+ parameters.idGoal = this.goal.goalId;
+ parameters.method = this.goal.apiMethod;
+
+ this.isLoading = true;
+
+ piwikApi.fetch(parameters).then(function () {
+ var search = $location.search();
+ if (search
+ && search.subcategory
+ && search.subcategory == 'Goals_AddNewGoal'
+ && piwik.helper.isAngularRenderingThePage()) {
+ // when adding a goal for the first time we need to load manage goals page afterwards
+ reportingMenuModel.reloadMenuItems().then(function () {
+ $location.search('subcategory', 'Goals_ManageGoals');
+ self.isLoading = false;
+ });
+ } else {
+ location.reload();
+ }
+ }, function () {
+ scrollToTop();
+ self.isLoading = false;
+ });
+ };
+
+ this.changedTriggerType = function () {
+ if (!this.isManuallyTriggered() && !this.goal.patternType) {
+ this.goal.patternType = 'contains';
+ }
+ }
+
+ this.showListOfReports = function (shouldScrollToTop) {
+ this.showGoalList = true;
+ this.showEditGoal = false;
+ scrollToTop();
+ };
+
+ this.showAddEditForm = function () {
+ this.showGoalList = false;
+ this.showEditGoal = true;
+ };
+
+ this.createGoal = function () {
+ this.showAddEditForm();
+ initGoalForm('Goals.addGoal', _pk_translate('Goals_AddGoal'), '', 'url', '', 'contains', /*caseSensitive = */false, /*allowMultiple = */'0', '0');
+ scrollToTop();
+ }
+
+ this.editGoal = function (goalId) {
+ this.showAddEditForm();
+ var goal = piwik.goals[goalId];
+ initGoalForm("Goals.updateGoal", _pk_translate('Goals_UpdateGoal'), goal.name, goal.match_attribute, goal.pattern, goal.pattern_type, (goal.case_sensitive != '0'), goal.revenue, goal.allow_multiple, goalId);
+ scrollToTop();
+ };
+
+ this.deleteGoal = function (goalId) {
+ var goal = piwik.goals[goalId];
+
+ $('#confirm').find('h2').text(sprintf(_pk_translate('Goals_DeleteGoalConfirm'), '"' + goal.name + '"'));
+ piwikHelper.modalConfirm('#confirm', {yes: function () {
+ self.isLoading = true;
+
+ piwikApi.fetch({idGoal: goalId, method: 'Goals.deleteGoal'}).then(function () {
+ location.reload();
+ }, function () {
+ self.isLoading = false;
+ });
+
+ }});
+ };
+
+ this.showListOfReports(false);
+ }
+})(); \ No newline at end of file
diff --git a/plugins/Goals/angularjs/manage-goals/manage-goals.directive.js b/plugins/Goals/angularjs/manage-goals/manage-goals.directive.js
new file mode 100644
index 0000000000..4563ab2ff4
--- /dev/null
+++ b/plugins/Goals/angularjs/manage-goals/manage-goals.directive.js
@@ -0,0 +1,38 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+
+/**
+ * Usage:
+ * <div piwik-manage-goals>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikManageGoals', piwikManageGoals);
+
+ piwikManageGoals.$inject = ['piwik'];
+
+ function piwikManageGoals(piwik){
+
+ return {
+ restrict: 'A',
+ priority: 10,
+ controller: 'ManageGoalsController',
+ controllerAs: 'manageGoals',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, controller) {
+ if (attrs.showAddGoal) {
+ controller.createGoal();
+ } else if (attrs.showGoal) {
+ controller.editGoal(attrs.showGoal);
+ }
+
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/Goals/javascripts/goalsForm.js b/plugins/Goals/javascripts/goalsForm.js
deleted file mode 100644
index 2ab5944af8..0000000000
--- a/plugins/Goals/javascripts/goalsForm.js
+++ /dev/null
@@ -1,231 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-function showAddNewGoal() {
- hideForms();
- $(".entityAddContainer").show();
- showCancel();
- hideCreateGoal();
- piwikHelper.lazyScrollTo(".entityContainer", 400);
- return false;
-}
-
-function showEditGoals() {
- hideForms();
- showCreateGoal();
- $("#entityEditContainer").show();
- piwikHelper.lazyScrollTo(".entityContainer", 400);
- return false;
-}
-
-function hideForms() {
- $(".entityAddContainer").hide();
- $("#entityEditContainer").hide();
-}
-
-function showCancel() {
- $(".entityCancel").show();
- $('.entityCancelLink').click(function () {
- hideForms();
- $(".entityCancel").hide();
- showEditGoals();
- });
-}
-
-function showCreateGoal() {
- $("#add-goal").show();
-}
-
-function hideCreateGoal() {
- $("#add-goal").hide();
-}
-
-function onMatchAttributeChange(matchAttribute)
-{
- if ('event' === matchAttribute) {
- $('.entityAddContainer .whereEvent').show();
- $('.entityAddContainer .whereUrl').hide();
- } else {
- $('.entityAddContainer .whereEvent').hide();
- $('.entityAddContainer .whereUrl').show();
- }
-
- $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]);
- $('#examples_pattern').html(mappingMatchTypeExamples[matchAttribute]);
-}
-
-function updateMatchAttribute () {
- var matchTypeId = $(this).val();
- onMatchAttributeChange(matchTypeId);
-}
-
-// 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();
- $('#match_attribute_section2').hide();
- $('#manual_trigger_section').show();
- matchAttribute = 'url';
- } else {
- $('select[name=trigger_type] option[value=visitors]').prop('selected', true);
- }
-
- if (0 === matchAttribute.indexOf('event')) {
- $('select[name=event_type] option[value=' + matchAttribute + ']').prop('selected', true);
- matchAttribute = 'event';
- }
-
- onMatchAttributeChange(matchAttribute);
-
- $('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);
- }
-
- // force re-run of iCheck. They were already initialized with all radio fields not selected. see #5961
- $('.entityAddContainer div.form-radio').removeClass('form-radio');
- $(document).trigger('Goals.edit', {});
-}
-
-function bindGoalForm() {
-
- $('select[name=trigger_type]').change(function () {
- var triggerTypeId = $(this).val();
- if (triggerTypeId == "manually") {
- $('input[name=match_attribute]').prop('disabled', true);
- $('#match_attribute_section').hide();
- $('#match_attribute_section2').hide();
- $('#manual_trigger_section').show();
- } else {
- $('input[name=match_attribute]').removeProp('disabled');
- $('#match_attribute_section').show();
- $('#match_attribute_section2').show();
- $('#manual_trigger_section').hide();
- // force re-run of iCheck
- $('.entityAddContainer div.form-radio').removeClass('form-radio');
- $(document).trigger('Goals.edit', {});
- }
- });
-
- $(document).bind('Goals.edit', function () {
- $('input[name=match_attribute]').off('change', updateMatchAttribute);
- $('input[name=match_attribute]').change(updateMatchAttribute);
- });
-
- $('#goal_submit').click(function () {
- // prepare ajax query to API to add goal
- ajaxAddGoal();
- return false;
- });
-
- $('#add-goal').click(function () {
- initAndShowAddGoalForm();
- piwikHelper.lazyScrollTo('#goal_name');
- });
-}
-
-function ajaxDeleteGoal(idGoal) {
- piwikHelper.lazyScrollTo(".entityContainer", 400);
-
- var parameters = {};
- parameters.format = 'json';
- parameters.idGoal = idGoal;
- parameters.module = 'API';
- parameters.method = 'Goals.deleteGoal';
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(parameters, 'get');
- ajaxRequest.setLoadingElement('#goalAjaxLoading');
- ajaxRequest.setCallback(function () { location.reload(); });
- ajaxRequest.send(true);
-}
-
-function ajaxAddGoal() {
- piwikHelper.lazyScrollTo(".entityContainer", 400);
-
- var parameters = {};
- parameters.name = encodeURIComponent($('#goal_name').val());
-
- if ($('[name=trigger_type]').val() == 'manually') {
- parameters.matchAttribute = 'manually';
- parameters.patternType = 'regex';
- parameters.pattern = '.*';
- parameters.caseSensitive = 0;
- } else {
- parameters.matchAttribute = $('input[name=match_attribute]:checked').val();
-
- if (parameters.matchAttribute === 'event') {
- parameters.matchAttribute = $('select[name=event_type]').val();
- }
-
- parameters.patternType = $('[name=pattern_type]').val();
- 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.idGoal = $('input[name=goalIdUpdate]').val();
- parameters.format = 'json';
- parameters.module = 'API';
- parameters.method = $('input[name=methodGoalAPI]').val();
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(parameters, 'get');
- ajaxRequest.setLoadingElement('#goalAjaxLoading');
- ajaxRequest.setCallback(function () {
- location.reload();
- });
- ajaxRequest.send(true);
-}
-
-function editGoal(goalId)
-{
- var goal = piwik.goals[goalId];
- initGoalForm("Goals.updateGoal", _pk_translate('Goals_UpdateGoal'), goal.name, goal.match_attribute, goal.pattern, goal.pattern_type, (goal.case_sensitive != '0'), goal.revenue, goal.allow_multiple, goalId);
- showAddNewGoal();
-}
-
-function bindListGoalEdit() {
- $('.edit-goal').click(function () {
- var goalId = $(this).attr('id');
- editGoal(goalId);
- return false;
- });
-
- $('.delete-goal').click(function () {
- var goalId = $(this).attr('id');
- var goal = piwik.goals[goalId];
-
- $('#confirm').find('h2').text(sprintf(_pk_translate('Goals_DeleteGoalConfirm'), '"' + 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'), '', 'url', '', 'contains', /*caseSensitive = */false, /*allowMultiple = */'0', '0');
- return showAddNewGoal();
-}
diff --git a/plugins/Goals/lang/en.json b/plugins/Goals/lang/en.json
index 2b9a79ea32..38e4ac7c43 100644
--- a/plugins/Goals/lang/en.json
+++ b/plugins/Goals/lang/en.json
@@ -71,7 +71,6 @@
"IsExactly": "is exactly %s",
"LearnMoreAboutGoalTrackingDocumentation": "Learn more about %1$s Tracking Goals in Piwik%2$s in the user documentation.",
"LeftInCart": "%s left in cart",
- "ManageGoalsOrCreateANewGoal": "%1$sManage Goals%2$s or create a new Goal now!",
"Manually": "manually",
"ManuallyTriggeredUsingJavascriptFunction": "Goal is manually triggered using the JavaScript API trackGoal()",
"MatchesExpression": "matches the expression %s",
@@ -103,7 +102,7 @@
"VisitUrl": "Visit a given URL (page or group of pages)",
"WhenVisitors": "when visitors",
"WhereThe": "where the",
- "WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore": "where the visited page contains a call to the JavaScript 'trackGoal' method (%1$slearn more%2$s)",
+ "WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore": "The visited page needs to contain a call to the JavaScript 'trackGoal' method (%1$slearn more%2$s)",
"YouCanEnableEcommerceReports": "You can enable %1$s for this website in the %2$s page."
}
}
diff --git a/plugins/Goals/stylesheets/goals.css b/plugins/Goals/stylesheets/goals.css
index 16f594781a..e2307baedb 100644
--- a/plugins/Goals/stylesheets/goals.css
+++ b/plugins/Goals/stylesheets/goals.css
@@ -2,6 +2,10 @@
border-bottom: 1px dotted;
}
+.goalTriggerType .input-field {
+ margin-top: 0 !important;
+}
+
.goalEntry {
margin: 0 0 20px 0;
padding: 0 0 10px 0;
@@ -9,12 +13,9 @@
width: 614px;
}
-.managegoals .addrow:hover {
- text-decoration: underline;
-}
-
-.managegoals .addrow {
- margin-top: 0px;
+.addEditGoal .goalIsTriggeredWhen,
+.addEditGoal .whereTheMatchAttrbiute {
+ margin-bottom: 0;
}
/* dimension selector */
@@ -27,6 +28,10 @@ ul.ulGoalTopElements {
margin-left: 30px;
}
+ul.ulGoalTopElements li {
+ list-style-type: circle;
+}
+
.ulGoalTopElements a {
text-decoration: none;
color: #0033CC;
diff --git a/plugins/Goals/templates/_addEditGoal.twig b/plugins/Goals/templates/_addEditGoal.twig
index 79a9491afc..16be9c2424 100644
--- a/plugins/Goals/templates/_addEditGoal.twig
+++ b/plugins/Goals/templates/_addEditGoal.twig
@@ -1,67 +1,34 @@
{% import 'ajaxMacros.twig' as ajax %}
{{ ajax.errorDiv() }}
-{{ ajax.loadingDiv('goalAjaxLoading') }}
-<div class="entityContainer">
- {% if onlyShowAddNewGoal is not defined %}
- {% include "@Goals/_listGoalEdit.twig" %}
- {% endif %}
+<script type="text/javascript">
{% if userCanEditGoals %}
- {% include "@Goals/_formAddGoal.twig" %}
{% if onlyShowAddNewGoal is not defined %}
- <div class='entityCancel' style='display:none;'>
- {{ 'General_OrCancel'|translate("<a class='entityCancelLink'>","</a>")|raw }}
- </div>
-
+ piwik.goals = {{ goalsJSON|raw }};
{% endif %}
+ {% else %}
+ piwik.goals = {{ goalsJSON|raw }};
{% endif %}
- <a id='bottom'></a>
-</div>
-<script type="text/javascript">
- var mappingMatchTypeName = {
- "url": "{{ 'Goals_URL'|translate }}",
- "title": "{{ 'Goals_PageTitle'|translate }}",
- "file": "{{ 'Goals_Filename'|translate }}",
- "external_website": "{{ 'Goals_ExternalWebsiteUrl'|translate }}",
- "event": "{{ 'Events_Event'|translate }}"
- };
- var mappingMatchTypeExamples = {
- "url": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'checkout/confirmation'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://example.com/thank-you.html'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\/demo\\\/(.*)'") }}",
- "title": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'Order confirmation'") }}",
- "file": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'files/brochure.pdf'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://example.com/files/brochure.pdf'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\.zip'") }}",
- "external_website": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'amazon.com'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://mypartner.com/landing.html'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'") }}",
- "event": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'video'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'click'") }} \
- <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)_banner'") }}"
- };
- $(document).ready(function () {
- {% if userCanEditGoals %}
- bindGoalForm();
+</script>
+<div piwik-manage-goals
+ {% if userCanEditGoals %}
{% if onlyShowAddNewGoal is not defined %}
- piwik.goals = {{ goalsJSON|raw }};
- bindListGoalEdit();
-
{% if idGoal %}
- editGoal({{ idGoal|e('js') }});
- {% else %}
- showEditGoals();
+ show-goal="{{ idGoal|e('js') }}"
{% endif %}
{% else %}
- initAndShowAddGoalForm();
+ show-add-goal="true"
{% endif %}
- {% else %}
- piwik.goals = {{ goalsJSON|raw }};
- showEditGoals();
- {% endif %}
- });
+ {% endif %}>
-</script>
+ {% if onlyShowAddNewGoal is not defined %}
+ {% include "@Goals/_listGoalEdit.twig" %}
+ {% endif %}
+ {% if userCanEditGoals %}
+ {% include "@Goals/_formAddGoal.twig" %}
+ {% endif %}
+ <a id='bottom'></a>
+</div>
diff --git a/plugins/Goals/templates/_formAddGoal.twig b/plugins/Goals/templates/_formAddGoal.twig
index d8cdaf2ba4..2a3f43bbfe 100644
--- a/plugins/Goals/templates/_formAddGoal.twig
+++ b/plugins/Goals/templates/_formAddGoal.twig
@@ -1,109 +1,147 @@
-<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 }} </td>
- <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 }}</td>
- <td>
- <select name="trigger_type" class="inp">
- <option value="visitors">{{ 'Goals_WhenVisitors'|translate }}</option>
- <option value="manually">{{ 'Goals_Manually'|translate }}</option>
- </select>
-
- <p id="match_attribute_section2">
- <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_event" value="event" name="match_attribute"/>
- <label for="match_attribute_event">{{ 'Goals_SendEvent'|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>
- </p>
- </td>
- </tr>
- </tbody>
- <tbody id="match_attribute_section">
- <tr>
- <td class="first">{{ 'Goals_WhereThe'|translate }} <span class="whereUrl" id="match_attribute_name"></span><select name="event_type" class="whereEvent inp">
- <option value="event_category">{{ 'Events_EventCategory'|translate }}</option>
- <option value="event_action">{{ 'Events_EventAction'|translate }}</option>
- <option value="event_name">{{ 'Events_EventName'|translate }}</option>
- </select></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>
-
- {{ 'Goals_Pattern'|translate }}
-
- <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"/>
- <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">
+<div piwik-content-block
+ content-title="{{ 'Goals_AddNewGoal'|translate|e('html_attr') }}"
+ class="addEditGoal"
+ ng-show="manageGoals.showEditGoal">
+
+ {% if addNewGoalIntro is defined and addNewGoalIntro %}
+ {{ addNewGoalIntro|raw }}
+ {% endif %}
+
+ <div piwik-form>
+ <div piwik-field uicontrol="text" name="goal_name"
+ ng-model="manageGoals.goal.name"
+ title="{{ 'Goals_GoalName'|translate|e('html_attr') }}">
+ </div>
+
+ <div class="row goalIsTriggeredWhen">
+ <div class="col s12">
+ <h3>{{ 'Goals_GoalIsTriggered'|translate|e('html_attr') }}</h3>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col s12 m6 goalTriggerType">
+ <div piwik-field uicontrol="select" name="trigger_type"
+ ng-model="manageGoals.goal.triggerType"
+ ng-change="manageGoals.changedTriggerType()"
+ full-width="true"
+ options="{{ goalTriggerTypeOptions|json_encode }}">
+ </div>
+ </div>
+ <div class="col s12 m6">
+ <div piwik-alert="info" ng-show="manageGoals.goal.triggerType == 'manually'">
{{ '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>")|raw }}
- </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)|raw }}
- <div class="entityInlineHelp"> {{ 'Goals_DefaultRevenueHelp'|translate }} </div>
- </td>
- </tr>
- <tr>
- </tbody>
- </table>
- <input type="hidden" name="methodGoalAPI" value=""/>
+ </div>
+
+ <div piwik-field uicontrol="radio" name="match_attribute"
+ ng-show="manageGoals.goal.triggerType != 'manually'"
+ full-width="true"
+ ng-model="manageGoals.goal.matchAttribute"
+ options="{{ goalMatchAttributeOptions|json_encode }}">
+ </div>
+ </div>
+ </div>
+
+ <div class="row whereTheMatchAttrbiute" ng-show="manageGoals.goal.triggerType != 'manually'">
+ <h3 class="col s12">{{ 'Goals_WhereThe'|translate|e('html_attr') }}
+ <span ng-show="manageGoals.goal.matchAttribute == 'url'">
+ {{ 'Goals_URL'|translate }}
+ </span>
+ <span ng-show="manageGoals.goal.matchAttribute == 'title'">
+ {{ 'Goals_PageTitle'|translate }}
+ </span>
+ <span ng-show="manageGoals.goal.matchAttribute == 'file'">
+ {{ 'Goals_Filename'|translate }}
+ </span>
+ <span ng-show="manageGoals.goal.matchAttribute == 'external_website'">
+ {{ 'Goals_ExternalWebsiteUrl'|translate }}
+ </span>
+ </h3>
+ </div>
+
+ <div class="row" ng-show="manageGoals.goal.triggerType != 'manually'">
+ <div class="col s12 m6 l4"
+ ng-show="manageGoals.goal.matchAttribute == 'event'">
+ <div piwik-field uicontrol="select" name="event_type"
+ ng-model="manageGoals.goal.eventType"
+ full-width="true"
+ options="{{ eventTypeOptions|json_encode }}">
+ </div>
+ </div>
+
+ <div class="col s12 m6 l4">
+ <div piwik-field uicontrol="select" name="pattern_type"
+ ng-model="manageGoals.goal.patternType"
+ full-width="true"
+ options="{{ patternTypeOptions|json_encode }}">
+ </div>
+ </div>
+
+ <div class="col s12 m6 l4">
+ <div piwik-field uicontrol="text" name="pattern"
+ ng-model="manageGoals.goal.pattern"
+ title="{{ 'Goals_Pattern'|translate|e('html_attr') }}"
+ full-width="true">
+ </div>
+ </div>
+
+ <div id="examples_pattern" class="col s12" piwik-alert="info">
+ <span ng-show="manageGoals.goal.matchAttribute == 'url'">
+ {{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'checkout/confirmation'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://example.com/thank-you.html'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\/demo\\\/(.*)'") }}
+ </span>
+ <span ng-show="manageGoals.goal.matchAttribute == 'title'">
+ {{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'Order confirmation'") }}
+ </span>
+ <span ng-show="manageGoals.goal.matchAttribute == 'file'">
+ {{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'files/brochure.pdf'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://example.com/files/brochure.pdf'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\.zip'") }}
+ </span>
+ <span ng-show="manageGoals.goal.matchAttribute == 'external_website'">
+ {{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'amazon.com'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://mypartner.com/landing.html'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'") }}
+ </span>
+ <span ng-show="manageGoals.goal.matchAttribute == 'event'">
+ {{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'video'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'click'") }}
+ <br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)_banner'") }}"
+ </span>
+ </div>
+ </div>
+
+ <div piwik-field uicontrol="checkbox" name="case_sensitive"
+ ng-model="manageGoals.goal.caseSensitive"
+ ng-show="manageGoals.goal.triggerType != 'manually'"
+ title="{{ 'Goals_CaseSensitive'|translate|e('html_attr') }} {{ 'Goals_Optional'|translate|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="radio" name="allow_multiple"
+ ng-model="manageGoals.goal.allowMultiple"
+ options="{{ allowMultipleOptions|json_encode }}"
+ introduction="{{ 'Goals_AllowMultipleConversionsPerVisit'|translate|e('html_attr') }}"
+ inline-help="{{ 'Goals_HelpOneConversionPerVisit'|translate|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="text" name="revenue"
+ ng-model="manageGoals.goal.revenue"
+ introduction="{{ 'Goals_DefaultRevenue'|translate }} {{ 'Goals_Optional'|translate }}"
+ inline-help="{{ 'Goals_DefaultRevenueHelp'|translate|e('html_attr') }}">
+ </div>
+
<input type="hidden" name="goalIdUpdate" value=""/>
- <input type="submit" value="" name="submit" id="goal_submit" class="submit"/>
- </form>
+ <div piwik-save-button
+ saving="manageGoals.isLoading"
+ onconfirm="manageGoals.save()"
+ ng-value="manageGoals.goal.submitText"></div>
+
+ {% if onlyShowAddNewGoal is not defined %}
+ <div class='entityCancel' ng-show="manageGoals.showEditGoal" ng-click="manageGoals.showListOfReports()">
+ {{ 'General_OrCancel'|translate("<a class='entityCancelLink'>","</a>")|raw }}
+ </div>
+ {% endif %}
+ </div>
</div>
diff --git a/plugins/Goals/templates/_listGoalEdit.twig b/plugins/Goals/templates/_listGoalEdit.twig
index 3275bbc393..40b4d9639c 100644
--- a/plugins/Goals/templates/_listGoalEdit.twig
+++ b/plugins/Goals/templates/_listGoalEdit.twig
@@ -1,5 +1,26 @@
-<div id='entityEditContainer' class="managegoals" style="display:none;">
- <table class="dataTable entityTable">
+<div id='entityEditContainer' feature="true"
+ ng-show="manageGoals.showGoalList"
+ piwik-content-block content-title="{{ 'Goals_ManageGoals'|translate|e('html_attr') }}"
+ class="managegoals">
+
+ <div piwik-activity-indicator loading="manageGoals.isLoading"></div>
+
+ <div class="contentHelp">
+ {{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}
+
+ {% if not ecommerceEnabled %}
+ <br /><br/>
+ {% set websiteManageText %}
+ <a href='{{ linkTo({'module':'SitesManager','action':'index' }) }}'>{{ 'SitesManager_WebsitesManagement'|translate }}</a>
+ {% endset %}
+ {% set ecommerceReportText %}
+ <a href="http://piwik.org/docs/ecommerce-analytics/" rel="noreferrer" target="_blank">{{ 'Goals_EcommerceReports'|translate }}</a>
+ {% endset %}
+ {{ 'Goals_Optional'|translate }} {{ 'Goals_Ecommerce'|translate }}: {{ 'Goals_YouCanEnableEcommerceReports'|translate(ecommerceReportText,websiteManageText)|raw }}
+ {% endif %}
+ </div>
+
+ <table piwik-content-table>
<thead>
<tr>
<th class="first">Id</th>
@@ -31,17 +52,17 @@
{{ 'Goals_Pattern'|translate }} {{ goal.pattern_type }}: {{ goal.pattern }}
{% endif %}
</td>
- <td class="text-center">
+ <td class="center">
{% if goal.revenue==0 %}-{% else %}{{ goal.revenue|money(idSite)|raw }}{% endif %}
</td>
{% if userCanEditGoals %}
- <td class="text-center">
- <button id="{{ goal.idgoal }}" class="edit-goal btn btn-flat btn-lg" title="{{ 'General_Edit'|translate }}">
+ <td style="padding-top:2px">
+ <button ng-click="manageGoals.editGoal({{ goal.idgoal }})" class="table-action" title="{{ 'General_Edit'|translate }}">
<span class="icon-edit"></span>
</button>
</td>
- <td class="text-center">
- <button id="{{ goal.idgoal }}" class="delete-goal btn btn-flat btn-lg" title="{{ 'General_Delete'|translate }}">
+ <td style="padding-top:2px">
+ <button ng-click="manageGoals.deleteGoal({{ goal.idgoal }})" class="table-action" title="{{ 'General_Delete'|translate }}">
<span class="icon-delete"></span>
</button>
</td>
@@ -52,13 +73,14 @@
</table>
{% if userCanEditGoals and onlyShowAddNewGoal is not defined %}
- <p>
- <button id="add-goal" class="btn btn-lg btn-flat">
+ <div class="tableActionBar">
+ <button id="add-goal" ng-click="manageGoals.createGoal()">
<span class="icon-add"></span>
{{ 'Goals_AddNewGoal'|translate }}
</button>
- </p>
+ </div>
{% endif %}
+
</div>
<div class="ui-confirm" id="confirm">
diff --git a/plugins/Goals/templates/addNewGoal.twig b/plugins/Goals/templates/addNewGoal.twig
index 844892ecbc..9b7819d7f9 100644
--- a/plugins/Goals/templates/addNewGoal.twig
+++ b/plugins/Goals/templates/addNewGoal.twig
@@ -1,20 +1,21 @@
{% if userCanEditGoals %}
- <p>{{ 'Goals_NewGoalIntro'|translate }}</p>
- <p>{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}
- {{ 'Goals_ManageGoalsOrCreateANewGoal'|translate("<a href='#module=Goals&action=editGoals'>","</a>")|raw }}
- </p>
+ {% set addNewGoalIntro %}
+ <p>{{ 'Goals_NewGoalIntro'|translate }}</p>
+ <p>{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}</p>
+ {% endset %}
{% include "@Goals/_addEditGoal.twig" %}
<br/><br/>
{% else %}
- <h2>{{ 'Goals_AddNewGoal'|translate }}</h2>
- <p>
- {{ 'Goals_NoGoalsNeedAccess'|translate|raw }}
- </p>
- <p>
- {{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}
- </p>
+ <div piwik-content-block content-title="{{ 'Goals_AddNewGoal'|translate|e('html_attr') }}">
+ <p>
+ {{ 'Goals_NoGoalsNeedAccess'|translate|raw }}
+ </p>
+ <p>
+ {{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}
+ </p>
+ </div>
{% endif %}
diff --git a/plugins/Goals/templates/conversionOverview.twig b/plugins/Goals/templates/conversionOverview.twig
index a810c464aa..64f7512600 100644
--- a/plugins/Goals/templates/conversionOverview.twig
+++ b/plugins/Goals/templates/conversionOverview.twig
@@ -1,19 +1,22 @@
-<ul class="ulGoalTopElements">
- {% if topDimensions.country is defined %}
- <li>{{ 'Goals_BestCountries'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.country} %}</li>
- {% endif %}
- {% if topDimensions.keyword is defined and topDimensions.keyword|length > 0 %}
- <li>{{ 'Goals_BestKeywords'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.keyword} %}</li>
- {% endif %}
- {% if topDimensions.website is defined and topDimensions.website|length > 0 %}
- <li>{{ 'Goals_BestReferrers'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.website} %}</li>
- {% endif %}
- <li>
- {{ 'Goals_ReturningVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_returning~"</strong>")|raw }}
- , {{ 'Goals_NewVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_new~"</strong>")|raw }}
- </li>
-</ul>
+<div piwik-content-block
+ content-title="{{ 'Goals_ConversionsOverview'|translate|e('html_attr') }}">
+ <ul class="ulGoalTopElements">
+ {% if topDimensions.country is defined %}
+ <li>{{ 'Goals_BestCountries'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.country} %}</li>
+ {% endif %}
+ {% if topDimensions.keyword is defined and topDimensions.keyword|length > 0 %}
+ <li>{{ 'Goals_BestKeywords'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.keyword} %}</li>
+ {% endif %}
+ {% if topDimensions.website is defined and topDimensions.website|length > 0 %}
+ <li>{{ 'Goals_BestReferrers'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.website} %}</li>
+ {% endif %}
+ <li>
+ {{ 'Goals_ReturningVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_returning~"</strong>")|raw }}
+ , {{ 'Goals_NewVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_new~"</strong>")|raw }}
+ </li>
+ </ul>
<a href="javascript:;" class="segmentedlog" onclick="SegmentedVisitorLog.show('Goals.getMetrics', 'visitConvertedGoalId=={{ idGoal }}', {})">
{{ 'Live_RowActionTooltipWithDimension'|translate('General_Goal'|translate) }}
</a>
-<br style="clear:left"/> \ No newline at end of file
+<br style="clear:left"/>
+</div> \ No newline at end of file
diff --git a/plugins/Goals/templates/manageGoals.twig b/plugins/Goals/templates/manageGoals.twig
index 0b83540317..3dd730028d 100644
--- a/plugins/Goals/templates/manageGoals.twig
+++ b/plugins/Goals/templates/manageGoals.twig
@@ -8,22 +8,6 @@
{% block content %}
- <h2 piwik-enriched-headline>
- <div class="inlineHelp">{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}
-
- {% if not ecommerceEnabled %}
- <br /><br/>
- {% set websiteManageText %}
- <a href='{{ linkTo({'module':'SitesManager','action':'index' }) }}'>{{ 'SitesManager_WebsitesManagement'|translate }}</a>
- {% endset %}
- {% set ecommerceReportText %}
- <a href="http://piwik.org/docs/ecommerce-analytics/" rel="noreferrer" target="_blank">{{ 'Goals_EcommerceReports'|translate }}</a>
- {% endset %}
- {{ 'Goals_Optional'|translate }} {{ 'Goals_Ecommerce'|translate }}: {{ 'Goals_YouCanEnableEcommerceReports'|translate(ecommerceReportText,websiteManageText)|raw }}
- {% endif %}</div>
- {{ title }}
- </h2>
-
{% include "@Goals/_addEditGoal.twig" %}
<style type="text/css">
diff --git a/plugins/Insights/Visualizations/Insight.php b/plugins/Insights/Visualizations/Insight.php
index 91d2c90b63..349f269e51 100644
--- a/plugins/Insights/Visualizations/Insight.php
+++ b/plugins/Insights/Visualizations/Insight.php
@@ -25,7 +25,7 @@ class Insight extends Visualization
const ID = 'insightsVisualization';
const TEMPLATE_FILE = '@Insights/insightVisualization.twig';
const FOOTER_ICON_TITLE = 'Insights';
- const FOOTER_ICON = 'plugins/Insights/images/idea.png';
+ const FOOTER_ICON = 'icon-insights';
public function beforeLoadDataTable()
{
diff --git a/plugins/Insights/javascripts/insightsDataTable.js b/plugins/Insights/javascripts/insightsDataTable.js
index 42572ef93f..98bd2ab284 100644
--- a/plugins/Insights/javascripts/insightsDataTable.js
+++ b/plugins/Insights/javascripts/insightsDataTable.js
@@ -50,6 +50,8 @@
this.initComparedToXPeriodsAgo(domElem);
this.initFilterBy(domElem);
this.setFixWidthToMakeEllipsisWork(domElem);
+
+ $(domElem).find('select').material_select();
},
setFixWidthToMakeEllipsisWork: function (domElem) {
@@ -57,6 +59,42 @@
if (width) {
$('td.label', domElem).width(parseInt(width * 0.50, 10));
}
+
+ var showScrollbarIfMoreThanThisPxOverlap = 32;
+ this.overflowContentIfNeeded(domElem, showScrollbarIfMoreThanThisPxOverlap);
+
+ var self = this;
+
+ if (!this.windowResizeTableAttached) {
+ this.windowResizeTableAttached = true;
+
+ // on resize of the window we re-calculate everything.
+ var timeout = null;
+ var resizeDataTable = function () {
+
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+
+ timeout = setTimeout(function () {
+ var isInDom = domElem && domElem[0] && document && document.body && document.body.contains(domElem[0]);
+ if (isInDom) {
+ // as domElem might have been removed by now we check whether domElem actually still is in dom
+ // and do this expensive operation only if needed.
+ $('td.label', domElem).width('');
+ self.setFixWidthToMakeEllipsisWork(domElem);
+ } else {
+ $(window).off('resize', resizeDataTable);
+ }
+
+ timeout = null;
+ }, Math.floor((Math.random() * 80) + 220));
+ // we randomize it just a little to not process all dataTables at similar time but to have a little
+ // delay in between for smoother resizing. we want to do it between 300 and 400ms
+ }
+
+ $(window).on('resize', resizeDataTable);
+ }
},
_changeParameter: function (params) {
diff --git a/plugins/Insights/stylesheets/insightVisualization.less b/plugins/Insights/stylesheets/insightVisualization.less
index 75a7fddcf7..b18db6b893 100644
--- a/plugins/Insights/stylesheets/insightVisualization.less
+++ b/plugins/Insights/stylesheets/insightVisualization.less
@@ -8,16 +8,9 @@
.insightsDataTable {
.controls {
- padding: 10px;
- padding-bottom: 0px;
- font-size: 12px;
+ padding: 20px 10px 0 0;
margin-bottom: 10px;
- }
-
- .controlSeparator {
- height: 1px;
- border: 0px;
- background-color: #cccccc;
+ border-top: 1px solid @theme-color-border;
}
th.orderBy {
diff --git a/plugins/Insights/templates/insightControls.twig b/plugins/Insights/templates/insightControls.twig
index c0dfa3d70c..51c4a76d39 100644
--- a/plugins/Insights/templates/insightControls.twig
+++ b/plugins/Insights/templates/insightControls.twig
@@ -5,61 +5,51 @@
{{ 'Insights_ControlComparedToDescription'|translate }}
{% if period == 'day' %}
- <select size="1" name="comparedToXPeriodsAgo">
- <option value="1" {% if properties.compared_to_x_periods_ago == 1 %}selected{% endif %}>
- {{ 'Insights_DayComparedToPreviousDay'|translate }}
- </option>
- <option value="7" {% if properties.compared_to_x_periods_ago == 7 %}selected{% endif %}>
- {{ 'Insights_DayComparedToPreviousWeek'|translate }}
- </option>
- <option value="365" {% if properties.compared_to_x_periods_ago == 365 %}selected{% endif %}>
- {{ 'Insights_DayComparedToPreviousYear'|translate }}
- </option>
- </select>
+ <div class="row">
+ <div class="col s12 m6 l4 input-field">
+ <select size="1" name="comparedToXPeriodsAgo">
+ <option value="1" {% if properties.compared_to_x_periods_ago == 1 %}selected{% endif %}>{{ 'Insights_DayComparedToPreviousDay'|translate }}</option>
+ <option value="7" {% if properties.compared_to_x_periods_ago == 7 %}selected{% endif %}>{{ 'Insights_DayComparedToPreviousWeek'|translate }}</option>
+ <option value="365" {% if properties.compared_to_x_periods_ago == 365 %}selected{% endif %}>{{ 'Insights_DayComparedToPreviousYear'|translate }}</option>
+ </select>
+ </div>
+ </div>
{% elseif period == 'month' %}
- <select size="1" name="comparedToXPeriodsAgo">
- <option value="1" {% if properties.compared_to_x_periods_ago == 1 %}selected{% endif %}>
- {{ 'Insights_MonthComparedToPreviousMonth'|translate }}
- </option>
- <option value="12" {% if properties.compared_to_x_periods_ago == 12 %}selected{% endif %}>
- {{ 'Insights_MonthComparedToPreviousYear'|translate }}
- </option>
- </select>
+ <div class="row">
+ <div class="col s12 m6 l4 input-field">
+ <select size="1" name="comparedToXPeriodsAgo">
+ <option value="1" {% if properties.compared_to_x_periods_ago == 1 %}selected{% endif %}>{{ 'Insights_MonthComparedToPreviousMonth'|translate }}</option>
+ <option value="12" {% if properties.compared_to_x_periods_ago == 12 %}selected{% endif %}>{{ 'Insights_MonthComparedToPreviousYear'|translate }}</option>
+ </select>
+ </div>
+ </div>
{% elseif period == 'week' %}
{{ 'Insights_WeekComparedToPreviousWeek'|translate }}
{% elseif period == 'year' %}
{{ 'Insights_YearComparedToPreviousYear'|translate }}
{% endif %}
- {% endif %}
-
- <hr class="controlSeparator" />
+ {% endif %}
+ </div>
{{ 'Insights_Filter'|translate }}
- <select size="1" name="filterBy" title="{{ 'Insights_ControlFilterByDescription'|translate|e('html_attr') }}">
- <option {% if not properties.filter_by %}selected{% endif %} value="all">
- {{ 'General_All'|translate }}
- </option>
- <option {% if properties.filter_by == 'movers' %}selected{% endif %} value="movers">
- {{ 'Insights_FilterOnlyMovers'|translate }}
- </option>
- <option {% if properties.filter_by == 'new' %}selected{% endif %} value="new">
- {{ 'Insights_FilterOnlyNew'|translate }}
- </option>
- <option {% if properties.filter_by == 'disappeared' %}selected{% endif %} value="disappeared">
- {{ 'Insights_FilterOnlyDisappeared'|translate }}
- </option>
- </select>
+ <div class="row">
+ <div class="col s12 m4 l4 input-field">
+ <select size="1" name="filterBy" title="{{ 'Insights_ControlFilterByDescription'|translate|e('html_attr') }}">
+ <option {% if not properties.filter_by %}selected{% endif %} value="all">{{ 'General_All'|translate }}</option>
+ <option {% if properties.filter_by == 'movers' %}selected{% endif %} value="movers">{{ 'Insights_FilterOnlyMovers'|translate }}</option>
+ <option {% if properties.filter_by == 'new' %}selected{% endif %} value="new">{{ 'Insights_FilterOnlyNew'|translate }}</option>
+ <option {% if properties.filter_by == 'disappeared' %}selected{% endif %} value="disappeared">{{ 'Insights_FilterOnlyDisappeared'|translate }}</option>
+ </select>
+ </div>
+
+ <div class="col s12 m6 l4 input-field">
+ <select size="1" name="showIncreaseOrDecrease" title="Show increaser and/or decreaser">
+ <option value="both" {% if properties.limit_increaser and properties.limit_decreaser %}selected{%endif%}>{{ 'Insights_FilterIncreaserAndDecreaser'|translate }}</option>
+ <option value="increase" {% if properties.limit_increaser and not properties.limit_decreaser %}selected{%endif%}>{{ 'Insights_FilterOnlyIncreaser'|translate }}</option>
+ <option value="decrease" {% if not properties.limit_increaser and properties.limit_decreaser %}selected{%endif%}>{{ 'Insights_FilterOnlyDecreaser'|translate }}</option>
+ </select>
+ </div><div class="col s12 m1 l4">&nbsp;</div>
- <select size="1" name="showIncreaseOrDecrease" title="Show increaser and/or decreaser">
- <option value="both" {% if properties.limit_increaser and properties.limit_decreaser %}selected{%endif%}>
- {{ 'Insights_FilterIncreaserAndDecreaser'|translate }}
- </option>
- <option value="increase" {% if properties.limit_increaser and not properties.limit_decreaser %}selected{%endif%}>
- {{ 'Insights_FilterOnlyIncreaser'|translate }}
- </option>
- <option value="decrease" {% if not properties.limit_increaser and properties.limit_decreaser %}selected{%endif%}>
- {{ 'Insights_FilterOnlyDecreaser'|translate }}
- </option>
- </select>
+ </div>
</div> \ No newline at end of file
diff --git a/plugins/Insights/templates/table_row.twig b/plugins/Insights/templates/table_row.twig
index ab681e0850..da504f4382 100644
--- a/plugins/Insights/templates/table_row.twig
+++ b/plugins/Insights/templates/table_row.twig
@@ -20,10 +20,10 @@
</td>
{% if row.getColumn('grown') %}
- <td>+{{ row.getColumn('difference') }}</td>
- <td class="grown">+{{ row.getColumn('growth_percent') }}</td>
+ <td class="column">+{{ row.getColumn('difference') }}</td>
+ <td class="column grown">+{{ row.getColumn('growth_percent') }}</td>
{% else %}
- <td>{{ row.getColumn('difference') }}</td>
- <td class="notGrown">{{ row.getColumn('growth_percent') }}</td>
+ <td class="column">{{ row.getColumn('difference') }}</td>
+ <td class="column notGrown">{{ row.getColumn('growth_percent') }}</td>
{% endif %}
</tr> \ No newline at end of file
diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php
index e91ab0355c..f201c15616 100644
--- a/plugins/Installation/Controller.php
+++ b/plugins/Installation/Controller.php
@@ -30,6 +30,7 @@ use Piwik\Plugins\UserCountry\LocationProvider;
use Piwik\Plugins\UsersManager\API as APIUsersManager;
use Piwik\ProxyHeaders;
use Piwik\SettingsPiwik;
+use Piwik\Theme;
use Piwik\Tracker\TrackerCodeGenerator;
use Piwik\Translation\Translator;
use Piwik\Updater;
@@ -403,7 +404,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
*/
Piwik::postEvent('Installation.defaultSettingsForm.init', array($form));
- $form->addElement('submit', 'submit', array('value' => Piwik::translate('General_ContinueToPiwik') . ' »', 'class' => 'btn btn-lg'));
+ $form->addElement('submit', 'submit', array('value' => Piwik::translate('General_ContinueToPiwik') . ' »', 'class' => 'btn'));
if ($form->validate()) {
try {
@@ -480,18 +481,60 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
}
/**
- * Prints out the CSS for installer/updater
+ * Return the base.less compiled to css
*
- * During installation and update process, we load a minimal Less file.
- * At this point Piwik may not be setup yet to write files in tmp/assets/
- * so in this case we compile and return the string on every request.
+ * @return string
*/
- public function getBaseCss()
+ public function getInstallationCss()
{
Common::sendHeader('Content-Type: text/css');
- return AssetManager::getInstance()->getCompiledBaseCss()->getContent();
+ Common::sendHeader('Cache-Control: max-age=' . (60 * 60));
+
+ $files = array(
+ 'plugins/Morpheus/stylesheets/base/bootstrap.css',
+ 'plugins/Morpheus/stylesheets/base/icons.css',
+ 'libs/jquery/themes/base/jquery-ui.min.css',
+ 'libs/bower_components/materialize/dist/css/materialize.min.css',
+ 'plugins/Morpheus/stylesheets/base.less',
+ 'plugins/Morpheus/stylesheets/general/_forms.less',
+ 'plugins/Installation/stylesheets/installation.css'
+ );
+
+ return AssetManager::compileCustomStylesheets($files);
}
+ /**
+ * Return the base.less compiled to css
+ *
+ * @return string
+ */
+ public function getInstallationJs()
+ {
+ Common::sendHeader('Content-Type: application/javascript; charset=UTF-8');
+ Common::sendHeader('Cache-Control: max-age=' . (60 * 60));
+
+ $files = array(
+ 'libs/bower_components/jquery/dist/jquery.min.js',
+ 'libs/bower_components/jquery-ui/ui/minified/jquery-ui.min.js',
+ 'libs/bower_components/materialize/dist/js/materialize.min.js',
+ 'libs/bower_components/angular/angular.min.js',
+ 'libs/bower_components/angular-sanitize/angular-sanitize.js',
+ 'libs/bower_components/angular-animate/angular-animate.js',
+ 'libs/bower_components/angular-cookies/angular-cookies.js',
+ 'libs/bower_components/ngDialog/js/ngDialog.min.js',
+ 'plugins/CoreHome/angularjs/common/services/service.module.js',
+ 'plugins/CoreHome/angularjs/common/filters/filter.module.js',
+ 'plugins/CoreHome/angularjs/common/filters/translate.js',
+ 'plugins/CoreHome/angularjs/common/directives/directive.module.js',
+ 'plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js',
+ 'plugins/CoreHome/angularjs/piwikApp.config.js',
+ 'plugins/CoreHome/angularjs/piwikApp.js',
+ 'plugins/Installation/javascripts/installation.js',
+ );
+
+ return AssetManager::compileCustomJs($files);
+ }
+
private function getParam($name)
{
return Common::getRequestVar($name, false, 'string');
diff --git a/plugins/Installation/FormDatabaseSetup.php b/plugins/Installation/FormDatabaseSetup.php
index 2a54f8722a..90f563d9fc 100644
--- a/plugins/Installation/FormDatabaseSetup.php
+++ b/plugins/Installation/FormDatabaseSetup.php
@@ -71,7 +71,7 @@ class FormDatabaseSetup extends QuickForm2
->loadOptions($adapters)
->addRule('required', Piwik::translate('General_Required', Piwik::translate('Installation_DatabaseSetupAdapter')));
- $this->addElement('submit', 'submit', array('value' => Piwik::translate('General_Next') . ' »', 'class' => 'btn btn-lg'));
+ $this->addElement('submit', 'submit', array('value' => Piwik::translate('General_Next') . ' »', 'class' => 'btn'));
$defaultDatabaseType = Config::getInstance()->database['type'];
$this->addElement( 'hidden', 'type')->setLabel('Database engine');
diff --git a/plugins/Installation/FormFirstWebsiteSetup.php b/plugins/Installation/FormFirstWebsiteSetup.php
index 50c7f0989f..c0491551cf 100644
--- a/plugins/Installation/FormFirstWebsiteSetup.php
+++ b/plugins/Installation/FormFirstWebsiteSetup.php
@@ -60,7 +60,7 @@ class FormFirstWebsiteSetup extends QuickForm2
1 => Piwik::translate('SitesManager_EnableEcommerce'),
));
- $this->addElement('submit', 'submit', array('value' => Piwik::translate('General_Next') . ' »', 'class' => 'btn btn-lg'));
+ $this->addElement('submit', 'submit', array('value' => Piwik::translate('General_Next') . ' »', 'class' => 'btn'));
// default values
$this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
diff --git a/plugins/Installation/FormSuperUser.php b/plugins/Installation/FormSuperUser.php
index c4b1d0944d..8c14e13716 100644
--- a/plugins/Installation/FormSuperUser.php
+++ b/plugins/Installation/FormSuperUser.php
@@ -77,7 +77,7 @@ class FormSuperUser extends QuickForm2
'content' => '&nbsp;&nbsp;' . $piwikProNewsletter,
));
- $this->addElement('submit', 'submit', array('value' => Piwik::translate('General_Next') . ' »', 'class' => 'btn btn-lg'));
+ $this->addElement('submit', 'submit', array('value' => Piwik::translate('General_Next') . ' »', 'class' => 'btn'));
// default values
$this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
diff --git a/plugins/Installation/Installation.php b/plugins/Installation/Installation.php
index 59a0b02e64..46e6122f6a 100644
--- a/plugins/Installation/Installation.php
+++ b/plugins/Installation/Installation.php
@@ -122,7 +122,7 @@ class Installation extends \Piwik\Plugin
private function isAllowedAction($action)
{
$controller = $this->getInstallationController();
- $isActionWhiteListed = in_array($action, array('saveLanguage', 'getBaseCss', 'reuseTables'));
+ $isActionWhiteListed = in_array($action, array('saveLanguage', 'getInstallationCss', 'getInstallationJs', 'reuseTables'));
return in_array($action, array_keys($controller->getInstallationSteps()))
|| $isActionWhiteListed;
diff --git a/plugins/Installation/ServerFilesGenerator.php b/plugins/Installation/ServerFilesGenerator.php
index 927f403ccb..c3c56f9a3c 100644
--- a/plugins/Installation/ServerFilesGenerator.php
+++ b/plugins/Installation/ServerFilesGenerator.php
@@ -37,7 +37,7 @@ class ServerFilesGenerator
"</IfModule>\n\n" .
"# Allow to serve static files which are safe\n" .
- "<Files ~ \"\\.(gif|ico|jpg|png|svg|js|css|htm|html|swf|mp3|mp4|wav|ogg|avi|ttf|eot)$\">\n" .
+ "<Files ~ \"\\.(gif|ico|jpg|png|svg|js|css|htm|html|swf|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2)$\">\n" .
$allow . "\n" .
"</Files>\n";
diff --git a/plugins/Installation/Widgets/GetSystemCheck.php b/plugins/Installation/Widgets/GetSystemCheck.php
new file mode 100644
index 0000000000..a3a4cd60be
--- /dev/null
+++ b/plugins/Installation/Widgets/GetSystemCheck.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Installation\Widgets;
+
+use Piwik\Piwik;
+use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult;
+use Piwik\Plugins\Diagnostics\DiagnosticReport;
+use Piwik\Plugins\Diagnostics\DiagnosticService;
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+use Piwik\View;
+
+class GetSystemCheck extends Widget
+{
+ /**
+ * @var DiagnosticService
+ */
+ private $diagnosticService;
+
+ public function __construct(DiagnosticService $diagnosticService)
+ {
+ $this->diagnosticService = $diagnosticService;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('About Piwik');
+ $config->setName('Installation_SystemCheck');
+ $config->setOrder(16);
+
+ $config->setIsEnabled(Piwik::hasUserSuperUserAccess());
+ }
+
+ public function render()
+ {
+ $report = $this->diagnosticService->runDiagnostics();
+ $numErrors = $report->getErrorCount();
+ $numWarnings = $report->getWarningCount();
+
+ $errors = array();
+ $warnings = array();
+
+ if ($report->hasErrors()) {
+ $errors = $this->getResults($report, DiagnosticResult::STATUS_ERROR);
+ }
+
+ if ($report->hasWarnings()) {
+ $warnings = $this->getResults($report, DiagnosticResult::STATUS_WARNING);
+ }
+
+ return $this->renderTemplate('getSystemCheckWidget', array(
+ 'numErrors' => $numErrors,
+ 'numWarnings' => $numWarnings,
+ 'errors' => $errors,
+ 'warnings' => $warnings,
+
+ ));
+ }
+
+ private function getResults(DiagnosticReport $report, $type)
+ {
+ $results = $report->getAllResults();
+
+ $reports = array();
+ foreach ($results as $result) {
+ if ($result->getStatus() === $type) {
+ $reports[] = $result;
+ }
+ }
+
+ return $reports;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/Installation/javascripts/installation.js b/plugins/Installation/javascripts/installation.js
index b63a200f8b..64b1f18da5 100644
--- a/plugins/Installation/javascripts/installation.js
+++ b/plugins/Installation/javascripts/installation.js
@@ -1,7 +1,23 @@
$(function () {
$('input:first').focus();
- $('code').click(function () { $(this).select(); });
+ $('code').click(function () {
+ $(this).select();
+ });
// Focus the first input field in the form
$('form:not(.filter) :input:visible:enabled:first').focus();
+
+ $('select').material_select();
+});
+
+$(document).ready(function() {
+ $('.form-help').each(function (index, help) {
+ var $help = $(help);
+ var $row = $help.parents('.row').first();
+
+ if ($row.size()) {
+ $help.addClass('col s12 m12 l6');
+ $row.append($help);
+ }
+ });
}); \ No newline at end of file
diff --git a/plugins/Installation/lang/en.json b/plugins/Installation/lang/en.json
index 0296e5f108..d4b2707162 100644
--- a/plugins/Installation/lang/en.json
+++ b/plugins/Installation/lang/en.json
@@ -88,6 +88,8 @@
"SystemCheckMbstringHelp": "The mbstring extension is required to handle multibyte characters in the User interface and API responses. Also please check that mbstring.func_overload is set to \"0\" in php.ini.",
"SystemCheckMemoryLimit": "Memory limit",
"SystemCheckMemoryLimitHelp": "On a high traffic website, the archiving process may require more memory than currently allowed. If necessary, change the memory_limit directive in your php.ini file.",
+ "SystemCheckNoErrorsOrWarnings": "There are no errors or warnings",
+ "SystemCheckViewFullSystemCheck": "View the full system check report",
"SystemCheckOpenURL": "Open URL",
"SystemCheckOpenURLHelp": "Newsletter subscriptions, update notifications, and one-click updates requires the \"curl\" extension, allow_url_fopen=On, or fsockopen() enabled.",
"SystemCheckOtherExtensions": "Other extensions",
diff --git a/plugins/Installation/stylesheets/installation.css b/plugins/Installation/stylesheets/installation.css
index feb21be950..716d4ee948 100644
--- a/plugins/Installation/stylesheets/installation.css
+++ b/plugins/Installation/stylesheets/installation.css
@@ -2,12 +2,31 @@ body {
font-size: 14px;
}
+#installation > .container {
+ width: 85% !important;
+}
+
+#installation strong {
+ font-weight: 700;
+}
+
/* Header */
.header {
padding: 50px 0 22px;
margin-bottom: 40px;
border-bottom: solid 1px #ccc;
}
+
+.content .btn {
+ margin-top: 10px;
+ margin-bottom: 20px;
+}
+
+.header .languageSelection {
+ color: #4183C4;
+ font-size: 13px;
+}
+
.logo {
float: left;
}
diff --git a/plugins/Installation/stylesheets/systemCheckPage.less b/plugins/Installation/stylesheets/systemCheckPage.less
index 5035e8cd2e..05c97b2cdb 100755
--- a/plugins/Installation/stylesheets/systemCheckPage.less
+++ b/plugins/Installation/stylesheets/systemCheckPage.less
@@ -30,3 +30,33 @@
color: #D73F36;
margin-right: 10px;
}
+
+.widgetBody.system-check {
+ .icon-ok,
+ .icon-warning,
+ .icon-error {
+ margin-right: 0;
+ }
+
+ ul {
+ li {
+ list-style-type: disc;
+ list-style-position: inside;
+ }
+ }
+}
+
+.system-success {
+ color: #1AA282 !important;
+ font-weight: bold;
+}
+
+.system-errors {
+ font-weight: bold;
+ color: #D73F36 !important;
+}
+
+.system-warnings {
+ font-weight: bold;
+ color: #DF9D27 !important;
+} \ No newline at end of file
diff --git a/plugins/Installation/templates/_systemCheckSection.twig b/plugins/Installation/templates/_systemCheckSection.twig
index a2d0b7610e..96e3fc3ae3 100755
--- a/plugins/Installation/templates/_systemCheckSection.twig
+++ b/plugins/Installation/templates/_systemCheckSection.twig
@@ -1,12 +1,12 @@
{% import _self as local %}
-<table class="simple-table system-check" id="systemCheckRequired">
+<table class="entityTable system-check" id="systemCheckRequired" piwik-content-table>
{{ local.diagnosticTable(diagnosticReport.getMandatoryDiagnosticResults()) }}
</table>
<h3>{{ 'Installation_Optional'|translate }}</h3>
-<table class="simple-table system-check" id="systemCheckOptional">
+<table class="entityTable system-check" id="systemCheckOptional" piwik-content-table>
{{ local.diagnosticTable(diagnosticReport.getOptionalDiagnosticResults()) }}
</table>
diff --git a/plugins/Installation/templates/getSystemCheckWidget.twig b/plugins/Installation/templates/getSystemCheckWidget.twig
new file mode 100644
index 0000000000..7f10dc06e1
--- /dev/null
+++ b/plugins/Installation/templates/getSystemCheckWidget.twig
@@ -0,0 +1,39 @@
+<div class="widgetBody system-check">
+ {% if not numErrors and not numWarnings %}
+ <p class="system-success"><span class="icon-ok"></span> {{ 'Installation_SystemCheckNoErrorsOrWarnings'|translate }}</p>
+ {% endif %}
+
+ {% if numErrors %}
+ <p class="system-errors"><span class="icon-error"></span> {{ 'General_Errors'|translate }}:</p>
+ <ul>
+ {% for error in errors %}
+ <li title="{{ error.getLongErrorMessage|e('html_attr') }}">{{ error.getLabel }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+
+ {% if numWarnings %}
+
+ <p class="system-warnings">
+
+ {% if numErrors %}
+ <br />
+ {% endif %}
+
+ <span class="icon-warning"></span> {{ 'General_Warnings'|translate }}:
+ </p>
+ <ul>
+ {% for warning in warnings %}
+ <li title="{{ warning.getLongErrorMessage|e('html_attr') }}">{{ warning.getLabel }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+
+ {% if numErrors or numWarnings %}
+ <p>
+ <br />
+ <a href="{{ linkTo({'module': 'Installation', 'action': 'systemCheckPage'}) }}"
+ >{{ 'Installation_SystemCheckViewFullSystemCheck'|translate }}</a>
+ </p>
+ {% endif %}
+</div> \ No newline at end of file
diff --git a/plugins/Installation/templates/layout.twig b/plugins/Installation/templates/layout.twig
index ba331db0a7..457837fe58 100644
--- a/plugins/Installation/templates/layout.twig
+++ b/plugins/Installation/templates/layout.twig
@@ -5,30 +5,12 @@
<meta name="robots" content="noindex,nofollow">
<title>Piwik &rsaquo; {{ 'Installation_Installation'|translate }}</title>
- <link rel="stylesheet" type="text/css" href="libs/jquery/themes/base/jquery-ui.min.css"/>
- <link rel="stylesheet" type="text/css" href="index.php?module=Installation&action=getBaseCss"/>
- <link rel="stylesheet" type="text/css" href="plugins/Installation/stylesheets/installation.css"/>
+ <link rel="stylesheet" type="text/css" href="index.php?module=Installation&action=getInstallationCss"/>
+ <script type="text/javascript" src="index.php?module=Installation&action=getInstallationJs"></script>
<link rel="shortcut icon" href="plugins/CoreHome/images/favicon.png"/>
-
- <script type="text/javascript" src="libs/bower_components/jquery/dist/jquery.min.js"></script>
- <script type="text/javascript" src="libs/bower_components/jquery-ui/ui/minified/jquery-ui.min.js"></script>
- <script type="text/javascript" src="libs/bower_components/angular/angular.min.js"></script>
- <script type="text/javascript" src="libs/bower_components/angular-sanitize/angular-sanitize.js"></script>
- <script type="text/javascript" src="libs/bower_components/angular-animate/angular-animate.js"></script>
- <script type="text/javascript" src="libs/bower_components/angular-cookies/angular-cookies.js"></script>
- <script type="text/javascript" src="libs/bower_components/ngDialog/js/ngDialog.min.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/services/service.module.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/filters/filter.module.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/filters/translate.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/directives/directive.module.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/piwikApp.config.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/angularjs/piwikApp.js"></script>
- <script type="text/javascript" src="plugins/Installation/javascripts/installation.js"></script>
</head>
-<!--[if (gte IE 9)|!(IE)]><!-->
-<body ng-app="app"><!--<![endif]-->
+<body ng-app="app" id="installation">
<div class="container">
@@ -55,7 +37,7 @@
</div>
<div class="row">
- <div class="col-sm-3">
+ <div class="col s3">
<ul class="list-group">
{% for stepId,stepName in allStepsTitle %}
{% if currentStepId > stepId %}
@@ -69,10 +51,10 @@
{% endfor %}
</ul>
</div>
- <div class="col-sm-9 content">
+ <div class="col s9 content">
{% set nextButton %}
<p class="next-step">
- <a class="btn btn-lg" href="{{ linkTo({'action':nextModuleName, 'token_auth':null, 'method':null }) }}">
+ <a class="btn" href="{{ linkTo({'action':nextModuleName, 'token_auth':null, 'method':null }) }}">
{{ 'General_Next'|translate }} &raquo;</a>
</p>
{% endset %}
diff --git a/plugins/Installation/templates/systemCheckPage.twig b/plugins/Installation/templates/systemCheckPage.twig
index db925107e2..86828b2de1 100755
--- a/plugins/Installation/templates/systemCheckPage.twig
+++ b/plugins/Installation/templates/systemCheckPage.twig
@@ -4,24 +4,26 @@
{% block content %}
- <h2 piwik-enriched-headline>{{ title }}</h2>
+ <div piwik-content-block content-title="{{ title|e('html_attr') }}" feature="true">
- {% if diagnosticReport.hasErrors() %}
- <div class="alert alert-danger">
- {{ 'Installation_SystemCheckSummaryThereWereErrors'|translate('<strong>','</strong>','<strong><em>','</em></strong>')|raw }}
- {{ 'Installation_SeeBelowForMoreInfo'|translate }}
- </div>
- {% elseif diagnosticReport.hasWarnings() %}
- <div class="alert alert-warning">
- {{ 'Installation_SystemCheckSummaryThereWereWarnings'|translate }}
- {{ 'Installation_SeeBelowForMoreInfo'|translate }}
- </div>
- {% else %}
- <div class="alert alert-success">
- {{ 'Installation_SystemCheckSummaryNoProblems'|translate }}
- </div>
- {% endif %}
+ {% if diagnosticReport.hasErrors() %}
+ <div class="alert alert-danger">
+ {{ 'Installation_SystemCheckSummaryThereWereErrors'|translate('<strong>','</strong>','<strong>','</strong>')|raw }}
+ {{ 'Installation_SeeBelowForMoreInfo'|translate }}
+ </div>
+ {% elseif diagnosticReport.hasWarnings() %}
+ <div class="alert alert-warning">
+ {{ 'Installation_SystemCheckSummaryThereWereWarnings'|translate }}
+ {{ 'Installation_SeeBelowForMoreInfo'|translate }}
+ </div>
+ {% else %}
+ <div class="alert alert-success">
+ {{ 'Installation_SystemCheckSummaryNoProblems'|translate }}
+ </div>
+ {% endif %}
+
+ {% include "@Installation/_systemCheckSection.twig" %}
+ </div>
- {% include "@Installation/_systemCheckSection.twig" %}
{% endblock %}
diff --git a/plugins/LanguagesManager/LanguagesManager.php b/plugins/LanguagesManager/LanguagesManager.php
index 2a468d550a..af4557de9b 100644
--- a/plugins/LanguagesManager/LanguagesManager.php
+++ b/plugins/LanguagesManager/LanguagesManager.php
@@ -33,7 +33,6 @@ class LanguagesManager extends \Piwik\Plugin
public function registerEvents()
{
return array(
- 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'Config.NoConfigurationFile' => 'initLanguage',
'Request.dispatchCoreAndPluginUpdatesScreen' => 'initLanguage',
@@ -45,11 +44,6 @@ class LanguagesManager extends \Piwik\Plugin
);
}
- public function getStylesheetFiles(&$stylesheets)
- {
- $stylesheets[] = "plugins/Morpheus/stylesheets/base.less";
- }
-
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/LanguagesManager/angularjs/languageselector/languageselector.directive.js";
diff --git a/plugins/LanguagesManager/angularjs/translationsearch/translationsearch.directive.html b/plugins/LanguagesManager/angularjs/translationsearch/translationsearch.directive.html
index c21cffd24a..b67dda0e19 100644
--- a/plugins/LanguagesManager/angularjs/translationsearch/translationsearch.directive.html
+++ b/plugins/LanguagesManager/angularjs/translationsearch/translationsearch.directive.html
@@ -1,6 +1,6 @@
<div>
- <p class="adminTable">
+ <p>
This page helps you to find existing translations that you can reuse in your Plugin.
If you want to know more about translations have a look at our <a href="http://developer.piwik.org/guides/internationalization" rel="noreferrer" target="_blank">Internationalization guide</a>.
Enter a search term to find translations and their corresponding keys:
@@ -11,7 +11,9 @@
<br />
<br />
- <table ng-show="translationSearch.searchTerm" class="entityTable dataTable" style="width: 800px;word-break: break-all;">
+ <table piwik-content-table
+ ng-show="translationSearch.searchTerm"
+ style="word-break: break-all;">
<thead>
<tr>
<th style="width:250px;">Key</th>
diff --git a/plugins/LanguagesManager/templates/getLanguagesSelector.twig b/plugins/LanguagesManager/templates/getLanguagesSelector.twig
index f841eb2b72..ecd881202b 100644
--- a/plugins/LanguagesManager/templates/getLanguagesSelector.twig
+++ b/plugins/LanguagesManager/templates/getLanguagesSelector.twig
@@ -1,20 +1,18 @@
-<span class="topBarElem">
- <div class="languageSelection"
- ng-cloak
- menu-title="{{ currentLanguageName|e('html_attr') }}"
- piwik-menudropdown>
- <a class="item"
- href="?module=Proxy&amp;action=redirect&amp;url=http://piwik.org/translations/">{{ 'LanguagesManager_AboutPiwikTranslations'|translate }}</a>
- {% for language in languages %}
- <a class="item {% if language.code == currentLanguageCode %}active{% endif %}"
- value="{{ language.code }}"
- title="{{ language.name }} ({{ language.english_name }})">{{ language.name }}</a>
- {% endfor %}
+<div class="languageSelection"
+ ng-cloak
+ menu-title="{{ currentLanguageName|e('html_attr') }}"
+ piwik-menudropdown>
+ <a class="item"
+ href="?module=Proxy&amp;action=redirect&amp;url=http://piwik.org/translations/">{{ 'LanguagesManager_AboutPiwikTranslations'|translate }}</a>
+ {% for language in languages %}
+ <a class="item {% if language.code == currentLanguageCode %}active{% endif %}"
+ value="{{ language.code }}"
+ title="{{ language.name }} ({{ language.english_name }})">{{ language.name }}</a>
+ {% endfor %}
- <form action="index.php?module=LanguagesManager&amp;action=saveLanguage" method="post">
- <input type="hidden" name="language" id="language">
- {# During installation token_auth is not set #}
- {% if token_auth is defined %}<input type="hidden" name="token_auth" value="{{ token_auth }}"/>{% endif %}
- </form>
- </div>
-</span>
+ <form action="index.php?module=LanguagesManager&amp;action=saveLanguage" method="post">
+ <input type="hidden" name="language" id="language">
+ {# During installation token_auth is not set #}
+ {% if token_auth is defined %}<input type="hidden" name="token_auth" value="{{ token_auth }}"/>{% endif %}
+ </form>
+</div>
diff --git a/plugins/LanguagesManager/templates/searchTranslation.twig b/plugins/LanguagesManager/templates/searchTranslation.twig
index 0f1f5f7e66..d2b040f1b9 100644
--- a/plugins/LanguagesManager/templates/searchTranslation.twig
+++ b/plugins/LanguagesManager/templates/searchTranslation.twig
@@ -4,8 +4,8 @@
{% block content %}
- <h2 piwik-enriched-headline>{{ title }}</h2>
-
- <div piwik-translation-search></div>
+ <div piwik-content-block content-title="{{ title|e('html_attr') }}" feature="true">
+ <div piwik-translation-search></div>
+ </div>
{% endblock %}
diff --git a/plugins/Live/Visualizations/VisitorLog.php b/plugins/Live/Visualizations/VisitorLog.php
index 8c8c1e8139..96ef746cb6 100644
--- a/plugins/Live/Visualizations/VisitorLog.php
+++ b/plugins/Live/Visualizations/VisitorLog.php
@@ -58,6 +58,8 @@ class VisitorLog extends Visualization
*/
public function beforeRender()
{
+ $this->config->show_as_content_block = false;
+ $this->config->title = Piwik::translate('Live_VisitorLog');
$this->config->disable_row_actions = true;
$this->config->datatable_js_type = 'VisitorLog';
$this->config->enable_sort = false;
diff --git a/plugins/Live/javascripts/live.js b/plugins/Live/javascripts/live.js
index 36b59326a8..bf386fdb88 100644
--- a/plugins/Live/javascripts/live.js
+++ b/plugins/Live/javascripts/live.js
@@ -268,7 +268,7 @@ $(function() {
scheduleAnotherRequest();
});
- ajaxRequest.send(true);
+ ajaxRequest.send();
};
var exports = require("piwik/Live");
diff --git a/plugins/Live/javascripts/visitorLog.js b/plugins/Live/javascripts/visitorLog.js
index eb69fee607..07cdd313f2 100644
--- a/plugins/Live/javascripts/visitorLog.js
+++ b/plugins/Live/javascripts/visitorLog.js
@@ -86,7 +86,7 @@
if (repeat.length) {
repeat.html((parseInt(repeat.html()) + 1) + "x");
} else {
- prevelement.find('>div').prepend($("<em>2x</em>").attr({'class': 'repeat', 'title': _pk_translate('Live_PageRefreshed')}));
+ prevelement.find('>div').prepend($("<span>2x</span>").attr({'class': 'repeat', 'title': _pk_translate('Live_PageRefreshed')}));
}
$(this).hide();
} else {
diff --git a/plugins/Live/stylesheets/live.less b/plugins/Live/stylesheets/live.less
index bf71c90c2c..ff9d7f02fc 100644
--- a/plugins/Live/stylesheets/live.less
+++ b/plugins/Live/stylesheets/live.less
@@ -9,6 +9,13 @@
}
}
+.theWidgetContent > h2:first-child {
+ color: @theme-color-headline-alternative !important;
+ .title {
+ color: @theme-color-headline-alternative !important;
+ }
+}
+
#visitsLive .settings {
border-bottom: 1px solid @color-silver-l90;
}
@@ -18,7 +25,7 @@
}
#visitsLive .datetime {
- background: #E4E2D7;
+ background: @theme-color-background-base;
border-top: 1px solid #d3d1c5;
margin: 0;
line-height: 20px;
@@ -69,9 +76,6 @@
.dataTableFeatures {
border-bottom: 0px;
}
- .expandDataTableFooterDrawer {
- display: none;
- }
}
}
@@ -179,7 +183,6 @@ ol.visitorLog li {
}
.simple-realtime-metric {
- font-style: italic;
font-weight: bold;
color: #333;
}
diff --git a/plugins/Live/templates/_dataTableViz_visitorLog.twig b/plugins/Live/templates/_dataTableViz_visitorLog.twig
index 37c1cac60f..b9463c2439 100644
--- a/plugins/Live/templates/_dataTableViz_visitorLog.twig
+++ b/plugins/Live/templates/_dataTableViz_visitorLog.twig
@@ -108,7 +108,7 @@
{% endset %}
{% set visitorRow %}
- <div class="card row">
+ <div class="card row hoverable">
{% if visitor.getColumn('visitorId') is not empty and not clientSideParameters.hideProfileLink %}
<a class="visitor-log-visitor-profile-link visitorLogTooltip" title="{{ 'Live_ViewVisitorProfile'|translate }}" data-visitor-id="{{ visitor.getColumn("visitorId") }}">
@@ -118,7 +118,7 @@
{% endif %}
{% set cycleIndex=cycleIndex+1 %}
- <div class="col-md-{% if displayVisitorsInOwnColumn %}3{% else %}4{% endif %}">
+ <div class="col s12 m{% if displayVisitorsInOwnColumn %}3{% else %}4{% endif %}">
<strong class="visitorLogTooltip" title="{% if visitor.getColumn('visitorType')=='new' %}{{ 'General_NewVisitor'|translate }}{% else %}{{ 'Live_VisitorsLastVisit'|translate(visitor.getColumn('daysSinceLastVisit')) }}{% endif %}">
{{ visitor.getColumn('serverDatePrettyFirstAction') }}
{% if isWidget %}<br/>{% else %}-{% endif %} {{ visitor.getColumn('serverTimePrettyFirstAction') }}</strong>
@@ -187,12 +187,12 @@ GPS (lat/long): {{ visitor.getColumn('latitude') }},{{ visitor.getColumn('longit
</div>
{% if displayVisitorsInOwnColumn %}
- <div class="col-md-2 own-visitor-column">
+ <div class="col s12 m2 own-visitor-column">
{{ visitorColumnContent }}
</div>
{% endif %}
- <div class="col-md-{% if displayVisitorsInOwnColumn %}7{% else %}8{% endif %} column {% if visitor.getColumn('visitConverted') and not isWidget %}highlightField{% endif %}">
+ <div class="col s12 m{% if displayVisitorsInOwnColumn %}7{% else %}8{% endif %} column {% if visitor.getColumn('visitConverted') and not isWidget %}highlightField{% endif %}">
{{ postEvent('Live.visitorLogViewBeforeActionsInfo', visitor) }}
<div class="visitor-log-page-list">
<strong>
diff --git a/plugins/Live/templates/getLastVisitsStart.twig b/plugins/Live/templates/getLastVisitsStart.twig
index 064a3545ae..640b53f075 100644
--- a/plugins/Live/templates/getLastVisitsStart.twig
+++ b/plugins/Live/templates/getLastVisitsStart.twig
@@ -9,7 +9,7 @@
<span style="display:none;" class="serverTimestamp">{{ visitor.serverTimestamp|raw }}</span>
{{ postEvent('Live.visitorLogWidgetViewBeforeVisitInfo', visitor) }}
{% set year = visitor.serverTimestamp|date('Y') %}
- {{ visitor.serverDatePretty|replace({(year): ' '}) }} - {{ visitor.serverTimePretty }} {% if visitor.visitDuration > 0 %}<em>({{ visitor.visitDurationPretty|raw }})</em>{% endif %}
+ {{ visitor.serverDatePretty|replace({(year): ' '}) }} - {{ visitor.serverTimePretty }} {% if visitor.visitDuration > 0 %}({{ visitor.visitDurationPretty|raw }}){% endif %}
{% if visitor.visitorId|default(false) is not empty %}
&nbsp; <a class="visits-live-launch-visitor-profile rightLink" title="{{ 'Live_ViewVisitorProfile'|translate }} {% if visitor.userId is not empty %}{{ visitor.userId|raw }}{% endif %}" data-visitor-id="{{ visitor.visitorId }}">
{% if visitor.userId is not empty %}<br/>{% endif %}
@@ -154,7 +154,7 @@
{% endfor %}
{% if visitor.actionDetails|length > maxPagesDisplayedByVisitor %}
- <em>({{ 'Live_MorePagesNotDisplayed'|translate }})</em>
+ ({{ 'Live_MorePagesNotDisplayed'|translate }})
{% endif %}
</div>
</li>
diff --git a/plugins/Live/templates/index.twig b/plugins/Live/templates/index.twig
index b0a9382b65..bf2ccbeedb 100644
--- a/plugins/Live/templates/index.twig
+++ b/plugins/Live/templates/index.twig
@@ -1,6 +1,9 @@
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
- var segment = broadcast.getValueFromUrl('segment');
+ var segment = broadcast.getValueFromHash('segment');
+ if (!segment) {
+ segment = broadcast.getValueFromUrl('segment');
+ }
$('#visitsLive').liveWidget({
interval: {{ liveRefreshAfterMs }},
diff --git a/plugins/Live/templates/indexVisitorLog.twig b/plugins/Live/templates/indexVisitorLog.twig
index e04a1a35b7..29e4749fb4 100644
--- a/plugins/Live/templates/indexVisitorLog.twig
+++ b/plugins/Live/templates/indexVisitorLog.twig
@@ -1,3 +1,5 @@
-<h2 piwik-enriched-headline>{{ 'Live_VisitorLog'|translate }}</h2>
+<div piwik-content-intro>
+ <h2 piwik-enriched-headline>{{ 'Live_VisitorLog'|translate }}</h2>
+</div>
{{ visitorLog|raw }}
diff --git a/plugins/LogViewer b/plugins/LogViewer
-Subproject 15d48dd1788f71bcf0a7578fe5664af6c450362
+Subproject e2b6cd803a59a47df17a4e33d614422fdc5cd77
diff --git a/plugins/Login/javascripts/login.js b/plugins/Login/javascripts/login.js
index b2c7d59162..573a3225b4 100755
--- a/plugins/Login/javascripts/login.js
+++ b/plugins/Login/javascripts/login.js
@@ -7,36 +7,34 @@
(function ($) {
$(function() {
- var switchForm = function (fromFormId, toFormId, message, callback) {
- var fromLoginInputId = '#' + fromFormId + '_login',
- toLoginInputId = '#' + toFormId + '_login',
- toPasswordInputId = '#' + toFormId + '_password',
- fromLoginNavId = '#' + fromFormId + '_nav',
- toLoginNavId = '#' + toFormId + '_nav';
+ var switchForm = function (fromFormId, toFormId) {
+ var fromFormSelector = '#' + fromFormId;
+ var toFormSelector = '#' + toFormId;
+
+ var fromLoginInputId = fromFormSelector + '_login',
+ toLoginInputId = toFormSelector + '_login',
+ toPasswordInputId = toFormSelector + '_password';
if ($(toLoginInputId).val() === '') {
$(toLoginInputId).val($(fromLoginInputId).val());
}
+ var contentFrom = $(fromFormSelector).parents('.contentForm').first();
+ var contentTo = $(toFormSelector).parents('.contentForm').first();
+
// 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
+ $(contentFrom).fadeOut(500, function () {
+ // focus on login or password control based on whether a login exists
+ Materialize.updateTextFields();
+
+ $(contentTo).fadeIn(500, function () {
+
if ($(toLoginInputId).val() === '') {
$(toLoginInputId).focus();
- }
- else {
+ } else {
$(toPasswordInputId).focus();
}
- if (callback) {
- callback();
- }
});
});
};
@@ -44,15 +42,14 @@
// 'lost your password?' on click
$('#login_form_nav').click(function (e) {
e.preventDefault();
- switchForm('login_form', 'reset_form', $('#lost_password_instructions').html());
+ switchForm('login_form', 'reset_form');
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', '');
+ switchForm('reset_form', 'login_form');
return false;
});
@@ -64,7 +61,7 @@
$('.loadingPiwik').hide();
var isSuccess = response.indexOf('message_error') === -1,
- fadeOutIds = '#message_container';
+ fadeOutIds = '.resetForm .message_container';
if (isSuccess) {
fadeOutIds += ',#reset_form,#reset_form_nav';
}
@@ -74,7 +71,7 @@
$('#alternate_reset_nav').show();
}
- $('#message_container').html(response).fadeIn(300);
+ $('.resetForm .message_container').html(response).fadeIn(300);
});
};
@@ -95,6 +92,8 @@
});
$('#login_form_login').focus();
+
+ Materialize.updateTextFields();
});
}(jQuery));
diff --git a/plugins/Login/lang/en.json b/plugins/Login/lang/en.json
index 89a06ab7e0..54c0bcb053 100644
--- a/plugins/Login/lang/en.json
+++ b/plugins/Login/lang/en.json
@@ -12,13 +12,15 @@
"LoginOrEmail": "Username or E-mail",
"LoginPasswordNotCorrect": "Wrong Username and password combination.",
"LostYourPassword": "Lost your password?",
+ "ChangeYourPassword": "Change your password",
"MailPasswordChangeBody": "Hi %1$s,\n\nA password reset request was received from %2$s. To confirm this password change so you can login with your new credentials, visit the following link:\n\n%3$s\n\nAttention: Changing the password will also change your token_auth. You can look up your new token_auth on your settings page.\n\nIf you are using your API token_auth in any external applications or for archiving, make sure to update the token_auth as requests to the API will fail otherwise.\n\nNote: this link will expire in 24 hours.\n\nAnd thank you for using Piwik!",
"MailTopicPasswordChange": "Confirm Password Change",
+ "NewPassword": "New password",
+ "NewPasswordRepeat": "New password (repeat)",
"PasswordChanged": "Your password has been changed.",
"PasswordRepeat": "Password (repeat)",
"PasswordsDoNotMatch": "Passwords do not match.",
"PluginDescription": "Provides authentication via username and password as well as password reset functionality. Authentication method can be changed by using another Login plugin such as LoginLdap available on the Marketplace.",
- "RememberMe": "Remember Me",
- "ResetPasswordInstructions": "Enter a new password for your account."
+ "RememberMe": "Remember Me"
}
} \ No newline at end of file
diff --git a/plugins/Login/stylesheets/login.less b/plugins/Login/stylesheets/login.less
index ae35e24efd..c3acd40afd 100644
--- a/plugins/Login/stylesheets/login.less
+++ b/plugins/Login/stylesheets/login.less
@@ -1,44 +1,22 @@
/* LOGO
***********************/
#loginPage {
+
#logo {
- float: none;
- margin: 100px auto 0 auto;
- width: 240px;
- position: relative;
- }
-
- #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);
- }
-
- #logo .description a {
- font: 16px/16px 'Patrick Hand';
- color: #666666;
- right: auto;
- text-decoration: none;
- }
+ padding-top: 5px;
- #logo .description .arrow {
- background: url(../../Morpheus/images/affix-arrow.png);
- width: 50px;
- height: 68px;
- position: absolute;
- left: -35px;
+ img.default-piwik-logo {
+ width: 112px;
+ }
+
+ img {
+ max-height: 30px;
+ }
}
- #logo img {
- border: 0;
- vertical-align: bottom;
- height: auto;
- width: 240px;
- margin-right: 20px;
+ .message_container {
+ margin-top: 16px;
+ margin-bottom: 16px;
}
/* LAYOUT
@@ -52,41 +30,28 @@
margin-left: 16px;
}
- .loginSection {
- background-color: @login-section-background;
- width: 420px;
- padding: 30px;
- margin: 50px auto 0 auto;
- 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);
- }
-
- .loginSection h1 {
- text-align: center;
- color: #666;
- margin: 0 0 30px 0;
- font-style: normal;
- font-weight: normal;
- font-size: 26px;
- line-height: 1;
- position: relative;
- }
-
/* FORM
***********************/
- .loginSection form {
- margin: 0 5px;
- padding: 15px;
- position: relative;
- }
- .loginSection form div {
- margin-bottom: 24px;
+ .loginSection {
+ margin-top: 32px;
+
+ .icon.prefix {
+ font-size: 1.5rem;
+ width: 2.5rem;
+ padding-top: 0.9rem;
+ }
+
+ .input-field {
+ .prefix ~ input {
+ margin-left: 2.5rem;
+ width: 80% !important;
+ overflow: hidden;
+ }
+ }
}
- .loginSection .submit {
+ .loginSection .btn {
margin-top: 0;
}
@@ -94,10 +59,12 @@
border: 0;
}
- .loginSection fieldset.actions {
+ .loginSection .actions {
line-height: 35px;
- width: 315px;
- margin-top: 10px;
+ }
+
+ #login_form {
+ margin-top: 16px;
}
#login_form_rememberme {
@@ -106,39 +73,6 @@
/* FIELDS
***********************/
- .loginSection form input[type="text"],
- .loginSection form input[type="password"] {
- font-size: 20px;
- padding: 10px 15px 10px 45px;
- margin: 0 0 15px 0;
- width: 100%;
- border: 1px solid #ccc;
- border-radius: 5px;
- color: #555;
- -moz-box-shadow: 0 1px 1px #ccc inset, 0 1px 0 @theme-color-background-base;
- -webkit-box-shadow: 0 1px 1px #ccc inset, 0 1px 0 @theme-color-background-base;
- box-shadow: 0 1px 1px #ccc inset, 0 1px 0 @theme-color-background-base;
- }
-
- #login_form_password,
- #reset_form_password,
- #reset_form_password_bis,
- #login_form_login,
- #reset_form_login {
- background: @theme-color-background-base url(../../Morpheus/images/login-sprite.png) no-repeat;
- outline: 0;
- }
-
- #login_form_password,
- #reset_form_password,
- #reset_form_password_bis {
- background-position: 10px -51px;
- }
-
- #login_form_login,
- #reset_form_login {
- background-position: 10px 11px;
- }
/* MESSAGE
***********************/
diff --git a/plugins/Login/templates/login.twig b/plugins/Login/templates/login.twig
index 0140418af4..27f14d420e 100644
--- a/plugins/Login/templates/login.twig
+++ b/plugins/Login/templates/login.twig
@@ -8,9 +8,6 @@
{{ parent() }}
<script type="text/javascript" src="libs/bower_components/jquery-placeholder/jquery.placeholder.js"></script>
- <!--[if lt IE 9]>
- <script src="libs/bower_components/html5shiv/dist/html5shiv.min.js"></script>
- <![endif]-->
<script type="text/javascript" src="libs/jquery/jquery.smartbanner.js"></script>
<link rel="stylesheet" type="text/css" href="libs/jquery/stylesheets/jquery.smartbanner.css" />
@@ -38,119 +35,154 @@
<div id="notificationContainer">
</div>
+ <nav class="blue-grey darken-3">
+ <div class="nav-wrapper">
+ <span id="logo" class="brand-logo center">
- <div id="logo">
- {% if isCustomLogo == false %}
- <a href="http://piwik.org" title="{{ linkTitle }}">
- {% endif %}
- {% if hasSVGLogo %}
- <img src='{{ logoSVG }}' title="{{ linkTitle }}" alt="Piwik" class="ie-hide"/>
- <!--[if lt IE 9]>
- {% endif %}
- <img src='{{ logoLarge }}' title="{{ linkTitle }}" alt="Piwik" />
- {% if hasSVGLogo %}<![endif]-->{% endif %}
+ {% if isCustomLogo == false %}
+ <a href="http://piwik.org" title="{{ linkTitle }}">
+ {% endif %}
+ {% if hasSVGLogo %}
+ <img src='{{ logoSVG }}' class="{% if not isCustomLogo %}default-piwik-logo{% endif %}" title="{{ linkTitle }}" alt="Piwik"/>
+ {% else %}
+ <img src='{{ logoLarge }}' title="{{ linkTitle }}" alt="Piwik" />
+ {% endif %}
- {% if isCustomLogo %}
- {% set poweredByPiwik %}
- <i><a href="http://piwik.org/" rel="noreferrer" target="_blank">{{ linkTitle }}</a></i>
- {% endset %}
- {% endif %}
+ {% if isCustomLogo == false %}
+ </a>
+ {% endif %}
- {% if isCustomLogo == false %}
- </a>
- <div class="description">
- <a href="http://piwik.org" title="{{ linkTitle }}">{{ linkTitle }}</a>
- <div class="arrow"></div>
- </div>
- {% endif %}
- </div>
+ </span>
+ </div>
+ </nav>
- <section class="loginSection">
+ <section class="loginSection row">
+ <div class="col s12 m6 push-m3 l4 push-l4">
{# untrusted host warning #}
{% if (isValidHost is defined and invalidHostMessage is defined and isValidHost == false) %}
{% include '@CoreHome/_warningInvalidHost.twig' %}
{% else %}
- <div id="message_container">
+ <div class="contentForm loginForm">
+ {% embed 'contentBlock.twig' with {'title': 'Login_LogIn'|translate} %}
+ {% block content %}
- {{ include('@Login/_formErrors.twig', {formErrors: form_data.errors } ) }}
+ <div class="message_container">
- {% if AccessErrorString %}
- <div class="message_error">
- <strong>{{ 'General_Error'|translate }}</strong>: {{ AccessErrorString|raw }}<br/>
+ {{ include('@Login/_formErrors.twig', {formErrors: form_data.errors } ) }}
+
+ {% if AccessErrorString %}
+ <div class="message_error">
+ <strong>{{ 'General_Error'|translate }}</strong>: {{ AccessErrorString|raw }}<br/>
+ </div>
+ {% endif %}
+
+ {% if infoMessage %}
+ <p class="message">{{ infoMessage|raw }}</p>
+ {% endif %}
</div>
- {% endif %}
- {% if infoMessage %}
- <p class="message">{{ infoMessage|raw }}</p>
- {% endif %}
+ <form {{ form_data.attributes|raw }} ng-non-bindable>
+ <div class="row">
+ <div class="col s12 input-field">
+ <input type="text" name="form_login" placeholder="" id="login_form_login" class="input" value="" size="20"
+ tabindex="10" autofocus="autofocus"/>
+ <label for="login_form_login"><i class="icon-user icon"></i> {{ 'General_Username'|translate }}</label>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col s12 input-field">
+ <input type="hidden" name="form_nonce" id="login_form_nonce" value="{{ nonce }}"/>
+ <input type="password" placeholder="" name="form_password" id="login_form_password" class="input" value="" size="20"
+ tabindex="20" />
+ <label for="login_form_password"><i class="icon-locked icon"></i> {{ 'General_Password'|translate }}</label>
+ </div>
+ </div>
+
+ <div class="row actions">
+ <div class="col s12">
+ <input name="form_rememberme" type="checkbox" id="login_form_rememberme" value="1" tabindex="90"
+ {% if form_data.form_rememberme.value %}checked="checked" {% endif %}/>
+ <label for="login_form_rememberme">{{ 'Login_RememberMe'|translate }}</label>
+ <input class="submit btn" id='login_form_submit' type="submit" value="{{ 'Login_LogIn'|translate }}"
+ tabindex="100"/>
+ </div>
+ </div>
+
+ </form>
+ <p id="nav">
+ {{ postEvent("Template.loginNav", "top") }}
+ <a id="login_form_nav" href="#"
+ title="{{ 'Login_LostYourPassword'|translate }}">{{ 'Login_LostYourPassword'|translate }}</a>
+ {{ postEvent("Template.loginNav", "bottom") }}
+ </p>
+
+ {% if isCustomLogo %}
+ <p id="piwik">
+ <i><a href="http://piwik.org/" rel="noreferrer" target="_blank">{{ linkTitle }}</a></i>
+ </p>
+ {% endif %}
+
+ {% endblock %}
+ {% endembed %}
</div>
- <form {{ form_data.attributes|raw }} ng-non-bindable>
- <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="{{ 'General_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" {% endif %}/>
- <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;" ng-non-bindable>
- <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" id="reset_form_password" class="input" value="" size="20"
- tabindex="20" autocomplete="off"
- placeholder="{{ 'General_Password'|translate }}"/>
-
- <input type="password" name="form_password_bis" id="reset_form_password_bis" class="input" value=""
- size="20" tabindex="30" autocomplete="off"
- placeholder="{{ 'Login_PasswordRepeat'|translate }}"/>
- </fieldset>
-
- <fieldset class="actions">
- <span class="loadingPiwik" style="display:none;">
- <img alt="Loading" src="plugins/Morpheus/images/loading-blue.gif"/>
- </span>
- <input class="submit" id='reset_form_submit' type="submit"
- value="{{ 'General_ChangePassword'|translate }}" tabindex="100"/>
- </fieldset>
-
- <input type="hidden" name="module" value="{{ loginModule }}"/>
- <input type="hidden" name="action" value="resetPassword"/>
- </form>
- <p id="nav">
- {{ postEvent("Template.loginNav", "top") }}
- <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>
- {{ postEvent("Template.loginNav", "bottom") }}
- </p>
- {% if poweredByPiwik is defined %}
- <p id="piwik">
- {{ poweredByPiwik }}
- </p>
- {% endif %}
- <div id="lost_password_instructions" style="display:none;">
- <p class="message">{{ 'Login_ResetPasswordInstructions'|translate }}</p>
+ <div class="contentForm resetForm" style="display:none;">
+ {% embed 'contentBlock.twig' with {'title': 'Login_ChangeYourPassword'|translate} %}
+ {% block content %}
+
+ <div class="message_container">
+ </div>
+
+ <form id="reset_form" ng-non-bindable>
+ <div class="row">
+ <div class="col s12 input-field">
+ <input type="hidden" name="form_nonce" id="reset_form_nonce" value="{{ nonce }}"/>
+ <input type="text" placeholder="" name="form_login" id="reset_form_login" class="input" value="" size="20"
+ tabindex="10"/>
+ <label for="reset_form_login">{{ 'Login_LoginOrEmail'|translate }}</label>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col s12 input-field">
+ <input type="password" placeholder="" name="form_password" id="reset_form_password" class="input" value="" size="20"
+ tabindex="20" autocomplete="off"/>
+ <label for="reset_form_password">{{ 'Login_NewPassword'|translate }}</label>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col s12 input-field">
+ <input type="password" placeholder="" name="form_password_bis" id="reset_form_password_bis" class="input" value=""
+ size="20" tabindex="30" autocomplete="off"/>
+ <label for="reset_form_password_bis">{{ 'Login_NewPasswordRepeat'|translate }}</label>
+ </div>
+ </div>
+
+ <div class="row actions">
+ <div class="col s12">
+ <input class="submit btn" id='reset_form_submit' type="submit"
+ value="{{ 'General_ChangePassword'|translate }}" tabindex="100"/>
+
+ <span class="loadingPiwik" style="display:none;">
+ <img alt="Loading" src="plugins/Morpheus/images/loading-blue.gif"/>
+ </span>
+ </div>
+ </div>
+
+ <input type="hidden" name="module" value="{{ loginModule }}"/>
+ <input type="hidden" name="action" value="resetPassword"/>
+ </form>
+ <p id="nav">
+ <a id="reset_form_nav" href="#"
+ title="{{ 'Mobile_NavigationBack'|translate }}">{{ 'General_Cancel'|translate }}</a>
+ <a id="alternate_reset_nav" href="#" style="display:none;"
+ title="{{'Login_LogIn'|translate}}">{{ 'Login_LogIn'|translate }}</a>
+ </p>
+ {% endblock %}
+ {% endembed %}
</div>
{% endif %}
+
</section>
{% endblock %}
diff --git a/plugins/LoginHttpAuth b/plugins/LoginHttpAuth
-Subproject 92978e17acd032cfcdfc1f4be4c40947b54b91e
+Subproject 08d0c34ab8038486038a3312ac6007278b2a1c2
diff --git a/plugins/MobileMessaging/API.php b/plugins/MobileMessaging/API.php
index 345c633b15..9e24700d8a 100644
--- a/plugins/MobileMessaging/API.php
+++ b/plugins/MobileMessaging/API.php
@@ -417,6 +417,7 @@ class API extends \Piwik\Plugin\API
public function getDelegatedManagement()
{
Piwik::checkUserHasSomeViewAccess();
- return Option::get(MobileMessaging::DELEGATED_MANAGEMENT_OPTION) == 'true';
+ $option = Option::get(MobileMessaging::DELEGATED_MANAGEMENT_OPTION);
+ return $option === 'true';
}
}
diff --git a/plugins/MobileMessaging/Controller.php b/plugins/MobileMessaging/Controller.php
index 7ba976c67e..97422efe5c 100644
--- a/plugins/MobileMessaging/Controller.php
+++ b/plugins/MobileMessaging/Controller.php
@@ -50,26 +50,9 @@ class Controller extends ControllerAdmin
*/
public function index()
{
- Piwik::checkUserHasSuperUserAccess();
-
- $view = new View('@MobileMessaging/index');
- $this->setManageVariables($view);
-
- return $view->render();
- }
-
- /**
- * Mobile Messaging Settings tab :
- * - set delegated management
- * - provide & validate SMS API credential
- * - add & activate phone numbers
- * - check remaining credits
- */
- public function userSettings()
- {
Piwik::checkUserIsNotAnonymous();
- $view = new View('@MobileMessaging/userSettings');
+ $view = new View('@MobileMessaging/index');
$this->setManageVariables($view);
return $view->render();
@@ -88,37 +71,61 @@ class Controller extends ControllerAdmin
$this->translator->translate('MobileMessaging_SettingsMenu')
));
$view->creditLeft = 0;
- $view->provider = '';
+ $currentProvider = '';
if ($view->credentialSupplied && $view->accountManagedByCurrentUser) {
- $view->provider = $mobileMessagingAPI->getSMSProvider();
+ $currentProvider = $mobileMessagingAPI->getSMSProvider();
$view->creditLeft = $mobileMessagingAPI->getCreditLeft();
}
+ $view->delegateManagementOptions = array(
+ array('key' => '0',
+ 'value' => Piwik::translate('General_No'),
+ 'description' => Piwik::translate('General_Default') . '. ' .
+ Piwik::translate('MobileMessaging_Settings_LetUsersManageAPICredential_No_Help')),
+ array('key' => '1',
+ 'value' => Piwik::translate('General_Yes'),
+ 'description' => Piwik::translate('MobileMessaging_Settings_LetUsersManageAPICredential_Yes_Help'))
+ );
+
$providers = array();
+ $providerOptions = array();
foreach (SMSProvider::findAvailableSmsProviders() as $provider) {
+ if (empty($currentProvider)) {
+ $currentProvider = $provider->getId();
+ }
$providers[$provider->getId()] = $provider->getDescription();
+ $providerOptions[$provider->getId()] = $provider->getId();
}
+ $view->provider = $currentProvider;
$view->smsProviders = $providers;
+ $view->smsProviderOptions = $providerOptions;
+
+ $defaultCountry = Common::getCountry(
+ LanguagesManager::getLanguageCodeForCurrentUser(),
+ true,
+ IP::getIpFromHeader()
+ );
+
+ $view->defaultCallingCode = '';
// construct the list of countries from the lang files
- $countries = array();
+ $countries = array(array('key' => '', 'value' => ''));
foreach ($this->regionDataProvider->getCountryList() as $countryCode => $continentCode) {
if (isset(CountryCallingCodes::$countryCallingCodes[$countryCode])) {
- $countries[$countryCode] = array(
- 'countryName' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode),
- 'countryCallingCode' => CountryCallingCodes::$countryCallingCodes[$countryCode],
+
+ if ($countryCode == $defaultCountry) {
+ $view->defaultCallingCode = CountryCallingCodes::$countryCallingCodes[$countryCode];
+ }
+
+ $countries[] = array(
+ 'key' => CountryCallingCodes::$countryCallingCodes[$countryCode],
+ 'value' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode)
);
}
}
$view->countries = $countries;
- $view->defaultCountry = Common::getCountry(
- LanguagesManager::getLanguageCodeForCurrentUser(),
- true,
- IP::getIpFromHeader()
- );
-
$view->phoneNumbers = $mobileMessagingAPI->getPhoneNumbers();
$this->setBasicVariablesView($view);
diff --git a/plugins/MobileMessaging/Menu.php b/plugins/MobileMessaging/Menu.php
index 3c12293ac3..cd5e16a56e 100644
--- a/plugins/MobileMessaging/Menu.php
+++ b/plugins/MobileMessaging/Menu.php
@@ -15,12 +15,14 @@ class Menu extends \Piwik\Plugin\Menu
{
public function configureAdminMenu(MenuAdmin $menu)
{
- if (Piwik::hasUserSuperUserAccess()) {
- $menu->addManageItem('MobileMessaging_SettingsMenu', $this->urlForAction('index'), $order = 35);
- }
+ $title = 'MobileMessaging_SettingsMenu';
+ $url = $this->urlForAction('index');
+ $order = 35;
- if (!Piwik::isUserIsAnonymous()) {
- $menu->addPersonalItem('MobileMessaging_SettingsMenu', $this->urlForAction('userSettings'), $order = 12);
+ if (Piwik::hasUserSuperUserAccess()) {
+ $menu->addSystemItem($title, $url, $order);
+ } else if (!Piwik::isUserIsAnonymous()) {
+ $menu->addPersonalItem($title, $url, $order);
}
}
}
diff --git a/plugins/MobileMessaging/MobileMessaging.php b/plugins/MobileMessaging/MobileMessaging.php
index 5db3a3a571..9ea465dd05 100644
--- a/plugins/MobileMessaging/MobileMessaging.php
+++ b/plugins/MobileMessaging/MobileMessaging.php
@@ -78,6 +78,7 @@ class MobileMessaging extends \Piwik\Plugin
'ScheduledReports.allowMultipleReports' => 'allowMultipleReports',
'ScheduledReports.sendReport' => 'sendReport',
'Template.reportParametersScheduledReports' => 'template_reportParametersScheduledReports',
+ 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
);
}
@@ -86,7 +87,9 @@ class MobileMessaging extends \Piwik\Plugin
*/
public function getJsFiles(&$jsFiles)
{
- $jsFiles[] = "plugins/MobileMessaging/javascripts/MobileMessagingSettings.js";
+ $jsFiles[] = "plugins/MobileMessaging/angularjs/delegate-mobile-messaging-settings/delegate-mobile-messaging-settings.controller.js";
+ $jsFiles[] = "plugins/MobileMessaging/angularjs/manage-sms-provider/manage-sms-provider.controller.js";
+ $jsFiles[] = "plugins/MobileMessaging/angularjs/manage-mobile-phone-numbers/manage-mobile-phone-numbers.controller.js";
}
public function getStylesheetFiles(&$stylesheets)
@@ -94,6 +97,13 @@ class MobileMessaging extends \Piwik\Plugin
$stylesheets[] = "plugins/MobileMessaging/stylesheets/MobileMessagingSettings.less";
}
+ public function getClientSideTranslationKeys(&$translationKeys)
+ {
+ $translationKeys[] = 'CoreAdminHome_SettingsSaveSuccess';
+ $translationKeys[] = 'MobileMessaging_Settings_InvalidActivationCode';
+ $translationKeys[] = 'MobileMessaging_Settings_PhoneActivated';
+ }
+
public function validateReportParameters(&$parameters, $reportType)
{
if (self::manageEvent($reportType)) {
@@ -200,7 +210,7 @@ class MobileMessaging extends \Piwik\Plugin
}
}
- public static function template_reportParametersScheduledReports(&$out)
+ public static function template_reportParametersScheduledReports(&$out, $context = '')
{
if (Piwik::isUserIsAnonymous()) {
return;
@@ -208,7 +218,17 @@ class MobileMessaging extends \Piwik\Plugin
$view = new View('@MobileMessaging/reportParametersScheduledReports');
$view->reportType = self::MOBILE_TYPE;
- $view->phoneNumbers = APIMobileMessaging::getInstance()->getActivatedPhoneNumbers();
+ $view->context = $context;
+ $numbers = APIMobileMessaging::getInstance()->getActivatedPhoneNumbers();
+
+ $phoneNumbers = array();
+ if (!empty($numbers)) {
+ foreach ($numbers as $number) {
+ $phoneNumbers[$number] = $number;
+ }
+ }
+
+ $view->phoneNumbers = $phoneNumbers;
$out .= $view->render();
}
diff --git a/plugins/MobileMessaging/SMSProvider/Clockwork.php b/plugins/MobileMessaging/SMSProvider/Clockwork.php
index 50a7f0e50b..afda1fed76 100644
--- a/plugins/MobileMessaging/SMSProvider/Clockwork.php
+++ b/plugins/MobileMessaging/SMSProvider/Clockwork.php
@@ -44,7 +44,7 @@ class Clockwork extends SMSProvider
<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>
</ul>
- <br/><em>About Clockwork: </em><ul>
+ <br/>About Clockwork: <ul>
<li>Clockwork gives you fast, reliable high quality worldwide SMS delivery, over 450 networks in every corner of the globe.
</li><li>Cost per SMS message is around ~0.08USD (0.06EUR).
</li><li>Most countries and networks are supported but we suggest you check the latest position on their coverage map <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/sms-coverage/">here</a>.
diff --git a/plugins/MobileMessaging/angularjs/delegate-mobile-messaging-settings/delegate-mobile-messaging-settings.controller.js b/plugins/MobileMessaging/angularjs/delegate-mobile-messaging-settings/delegate-mobile-messaging-settings.controller.js
new file mode 100644
index 0000000000..d7f6a9bfab
--- /dev/null
+++ b/plugins/MobileMessaging/angularjs/delegate-mobile-messaging-settings/delegate-mobile-messaging-settings.controller.js
@@ -0,0 +1,39 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('DelegateMobileMessagingSettingsController', DelegateMobileMessagingSettingsController);
+
+ DelegateMobileMessagingSettingsController.$inject = ['piwikApi', 'piwik'];
+
+ function DelegateMobileMessagingSettingsController(piwikApi, piwik) {
+
+ var self = this;
+ this.isLoading = false;
+
+ this.save = function () {
+ this.isLoading = true;
+
+ piwikApi.post(
+ {method: 'MobileMessaging.setDelegatedManagement'},
+ {delegatedManagement: (this.enabled == '1') ? 'true' : 'false'}
+ ).then(function () {
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
+ id: 'mobileMessagingSettings', context: 'success'
+ });
+ notification.scrollToNotification();
+
+ piwik.helper.redirect();
+ self.isLoading = false;
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/MobileMessaging/angularjs/manage-mobile-phone-numbers/manage-mobile-phone-numbers.controller.js b/plugins/MobileMessaging/angularjs/manage-mobile-phone-numbers/manage-mobile-phone-numbers.controller.js
new file mode 100644
index 0000000000..06a9d255f7
--- /dev/null
+++ b/plugins/MobileMessaging/angularjs/manage-mobile-phone-numbers/manage-mobile-phone-numbers.controller.js
@@ -0,0 +1,110 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ManageMobilePhoneNumbersController', ManageMobilePhoneNumbersController);
+
+ ManageMobilePhoneNumbersController.$inject = ['piwikApi', 'piwik'];
+
+ function ManageMobilePhoneNumbersController(piwikApi, piwikk) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+ this.isAddingPhonenumber = false;
+ this.canAddNumber = false;
+ this.isActivated = {};
+
+ this.validateActivationCode = function(phoneNumber, index) {
+ if (!this.validationCode || !this.validationCode[index] || this.validationCode[index] == '') {
+ return;
+ }
+
+ var verificationCode = this.validationCode[index];
+
+ var success = function (response) {
+
+ self.isChangingPhoneNumber = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+
+ if (!response || !response.value) {
+ var message = _pk_translate('MobileMessaging_Settings_InvalidActivationCode');
+ notification.show(message, {
+ context: 'error',
+ id: 'MobileMessaging_ValidatePhoneNumber'
+ });
+ }
+ else {
+ var message = _pk_translate('MobileMessaging_Settings_PhoneActivated')
+ notification.show(message, {
+ context: 'success',
+ id: 'MobileMessaging_ValidatePhoneNumber'
+ });
+
+ self.isActivated[index] = true;
+ }
+
+ notification.scrollToNotification();
+ };
+
+ this.isChangingPhoneNumber = true;
+
+ piwikApi.post(
+ {method: 'MobileMessaging.validatePhoneNumber'},
+ {phoneNumber: phoneNumber, verificationCode: verificationCode},
+ {placeat: '#invalidVerificationCodeAjaxError'}
+ ).then(success, function () {
+ self.isChangingPhoneNumber = false;
+ });
+
+ }
+
+ this.removePhoneNumber = function (phoneNumber) {
+ if (!phoneNumber) {
+ return;
+ }
+
+ this.isChangingPhoneNumber = true;
+
+ piwikApi.post(
+ {method: 'MobileMessaging.removePhoneNumber'},
+ {phoneNumber: phoneNumber},
+ {placeat: '#invalidVerificationCodeAjaxError'}
+ ).then(function () {
+ self.isChangingPhoneNumber = false;
+ piwik.helper.redirect();
+ }, function () {
+ self.isChangingPhoneNumber = false;
+ });
+ }
+
+ this.validateNewPhoneNumberFormat = function () {
+ this.showSuspiciousPhoneNumber = $.trim(this.newPhoneNumber).lastIndexOf('0', 0) === 0;
+ this.canAddNumber = !!this.newPhoneNumber && this.newPhoneNumber != '';
+ };
+
+ this.addPhoneNumber = function() {
+ var phoneNumber = '+' + this.countryCallingCode + this.newPhoneNumber;
+
+ if (this.canAddNumber && phoneNumber.length > 1) {
+ this.isAddingPhonenumber = true;
+
+ piwikApi.post(
+ {method: 'MobileMessaging.addPhoneNumber'},
+ {phoneNumber: phoneNumber},
+ {placeat: '#ajaxErrorAddPhoneNumber'}
+ ).then(function () {
+ self.isAddingPhonenumber = false;
+ piwik.helper.redirect();
+ }, function () {
+ self.isAddingPhonenumber = false;
+ });
+ }
+ }
+
+ }
+})(); \ No newline at end of file
diff --git a/plugins/MobileMessaging/angularjs/manage-sms-provider/manage-sms-provider.controller.js b/plugins/MobileMessaging/angularjs/manage-sms-provider/manage-sms-provider.controller.js
new file mode 100644
index 0000000000..482445f1b2
--- /dev/null
+++ b/plugins/MobileMessaging/angularjs/manage-sms-provider/manage-sms-provider.controller.js
@@ -0,0 +1,64 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ManageSmsProviderController', ManageSmsProviderController);
+
+ ManageSmsProviderController.$inject = ['piwikApi', 'piwik'];
+
+ function ManageSmsProviderController(piwikApi, piwik) {
+
+ var self = this;
+ this.isDeletingAccount = false;
+ this.isUpdatingAccount = false;
+ this.showAccountForm = false;
+ this.isUpdateAccountPossible = false;
+
+ function deleteApiAccount() {
+ self.isDeletingAccount = true;
+
+ piwikApi.fetch(
+ {method: 'MobileMessaging.deleteSMSAPICredential'},
+ {placeat: '#ajaxErrorManageSmsProviderSettings'}
+ ).then(function () {
+ self.isDeletingAccount = false;
+ piwik.helper.redirect();
+ }, function () {
+ self.isDeletingAccount = false;
+ });
+ }
+
+ this.showUpdateAccount = function () {
+ this.showAccountForm = true;
+ };
+
+ this.isUpdateAccountPossible = function () {
+ this.canBeUpdated = (!!this.apiKey && this.apiKey != '' && !!this.smsProvider);
+ return this.canBeUpdated;
+ }
+
+ this.updateAccount = function () {
+ if (this.isUpdateAccountPossible()) {
+ this.isUpdatingAccount = true;
+
+ piwikApi.post(
+ {method: 'MobileMessaging.setSMSAPICredential'},
+ {provider: this.smsProvider, apiKey: this.apiKey},
+ {placeat: '#ajaxErrorManageSmsProviderSettings'}
+ ).then(function () {
+ self.isUpdatingAccount = false;
+ piwik.helper.redirect();
+ }, function () {
+ self.isUpdatingAccount = false;
+ });
+ }
+ }
+
+ this.deleteAccount = function () {
+ piwikHelper.modalConfirm('#confirmDeleteAccount', {yes: deleteApiAccount});
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/MobileMessaging/javascripts/MobileMessagingSettings.js b/plugins/MobileMessaging/javascripts/MobileMessagingSettings.js
deleted file mode 100644
index 20e2184c89..0000000000
--- a/plugins/MobileMessaging/javascripts/MobileMessagingSettings.js
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-// 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) {
-
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
-
- $(phoneNumberActivatedSelector).hide();
- if (!response.value) {
- var message = $(invalidActivationCodeMsgSelector).html();
- notification.show(message, {
- context: 'error',
- id: 'MobileMessaging_ValidatePhoneNumber',
- style: {marginTop: '10px'}
- });
- }
- else {
- var message = $(phoneNumberActivatedSelector).html();
- notification.show(message, {
- context: 'success',
- id: 'MobileMessaging_ValidatePhoneNumber',
- style: {marginTop: '10px'}
- });
-
- $(verificationCodeContainer).remove();
- $(phoneNumberContainer).find(validatePhoneNumberSubmitSelector).remove();
- $(phoneNumberContainer).find(formDescriptionSelector).remove();
- }
-
- notification.scrollToNotification();
- };
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'MobileMessaging.validatePhoneNumber'
- }, 'GET');
- ajaxHandler.addParams({phoneNumber: phoneNumber, verificationCode: verificationCode}, 'POST');
- ajaxHandler.setCallback(success);
- ajaxHandler.setLoadingElement(ajaxLoadingSelector);
- ajaxHandler.setErrorElement(invalidVerificationCodeAjaxErrorSelector);
- ajaxHandler.send(true);
- }
- }
-
- function removePhoneNumber(event) {
- var phoneNumberContainer = $(event.target).parent();
- var phoneNumber = phoneNumberContainer.find(phoneNumberSelector).html();
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'MobileMessaging.removePhoneNumber'
- }, 'GET');
- ajaxHandler.addParams({phoneNumber: phoneNumber}, 'POST');
- ajaxHandler.redirectOnSuccess();
- 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 != '') {
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'MobileMessaging.addPhoneNumber'
- }, 'GET');
- ajaxHandler.addParams({phoneNumber: phoneNumber}, 'POST');
- ajaxHandler.redirectOnSuccess();
- 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() {
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'MobileMessaging.deleteSMSAPICredential'
- }, 'GET');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement(ajaxLoadingSelector);
- ajaxHandler.setErrorElement(ajaxErrorsSelector);
- ajaxHandler.send(true);
- }
-
- function updateApiAccount() {
-
- var provider = $(providersSelector + ' option:selected').val();
- var apiKey = $(apiKeySelector).val();
-
- if (apiKey != '') {
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'MobileMessaging.setSMSAPICredential'
- }, 'GET');
- ajaxHandler.addParams({provider: provider, apiKey: apiKey}, 'POST');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement(ajaxLoadingSelector);
- ajaxHandler.setErrorElement(ajaxErrorsSelector);
- ajaxHandler.send(true);
- }
- }
-
- function setDelegatedManagement(delegatedManagement) {
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'MobileMessaging.setDelegatedManagement'
- }, 'GET');
- ajaxHandler.addParams({delegatedManagement: delegatedManagement}, 'POST');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement(ajaxLoadingSelector);
- ajaxHandler.setErrorElement(ajaxErrorsSelector);
- ajaxHandler.send(true);
- }
-
- function getDelegatedManagement() {
- return $(delegatedManagementSelector + ':checked').val();
- }
-
- /************************************************************
- * Public data and methods
- ************************************************************/
-
- return {
-
- /**
- * Initialize UI events
- */
- initUIEvents: function () {
- initUIEvents();
- }
- };
-
-}());
-
-$(document).ready(function () {
- MobileMessagingSettings.initUIEvents();
-});
diff --git a/plugins/MobileMessaging/lang/en.json b/plugins/MobileMessaging/lang/en.json
index 3bc39147ef..8414a84ef6 100644
--- a/plugins/MobileMessaging/lang/en.json
+++ b/plugins/MobileMessaging/lang/en.json
@@ -9,10 +9,14 @@
"PluginDescription": "Create and download custom SMS reports and have them sent to your mobile on a daily, weekly or monthly basis.",
"Settings_APIKey": "API Key",
"Settings_CountryCode": "Country Code",
+ "Settings_SelectCountry": "Select country",
"Settings_CredentialNotProvided": "Before you can create and manage phone numbers, please connect Piwik to your SMS Account above.",
"Settings_CredentialNotProvidedByAdmin": "Before you can create and manage phone numbers, please ask your administrator to connect Piwik to an SMS Account.",
"Settings_CredentialProvided": "Your %s SMS API account is correctly configured!",
"Settings_DeleteAccountConfirm": "Are you sure you want to delete this SMS account?",
+ "Settings_DelegatedSmsProviderOnlyAppliesToYou": "The configured SMS provider will be only used by you and not by any other users.",
+ "Settings_DelegatedPhoneNumbersOnlyUsedByYou": "The configured phone numbers can be only seen and used by you and not by any other users.",
+ "Settings_EnterActivationCode": "Enter activation code",
"Settings_InvalidActivationCode": "Code entered was not valid, please try again.",
"Settings_LetUsersManageAPICredential": "Allow users to manage their own SMS provider",
"Settings_LetUsersManageAPICredential_No_Help": "All users are able to receive SMS Reports and will use your account's credits.",
@@ -21,7 +25,7 @@
"Settings_PhoneActivated": "Phone number validated! You can now receive SMS with your stats.",
"Settings_PhoneNumber": "Phone Number",
"Settings_PhoneNumbers_Add": "Add a new Phone Number",
- "Settings_PhoneNumbers_CountryCode_Help": "If you do not know the phone country code, look for your country here",
+ "Settings_PhoneNumbers_CountryCode_Help": "If you do not know the phone country code, look for your country here.",
"Settings_PhoneNumbers_Help": "Before receiving SMS (text messages) reports on a phone, the phone number must be entered below.",
"Settings_PhoneNumbers_HelpAdd": "When you click \"Add\", a SMS containing a code will be sent to the phone. The user receiving the code should then login to Piwik, click on Settings, then click on Mobile Messaging. After entering the code, the user will be able to receive text reports on his phone.",
"Settings_PleaseSignUp": "To create SMS reports and receive short text messages with your websites' stats on your mobile phone, please sign up with the SMS API and enter your information below.",
diff --git a/plugins/MobileMessaging/stylesheets/MobileMessagingSettings.less b/plugins/MobileMessaging/stylesheets/MobileMessagingSettings.less
index 028c8fa804..a8788228e7 100644
--- a/plugins/MobileMessaging/stylesheets/MobileMessagingSettings.less
+++ b/plugins/MobileMessaging/stylesheets/MobileMessagingSettings.less
@@ -2,6 +2,14 @@
list-style: circle;
margin-left: 17px;
line-height: 1.5em;
+
+ li {
+ list-style-type: disc;
+ }
+}
+
+#suspiciousPhoneNumber {
+ clear:left;
}
.providerDescription {
@@ -10,4 +18,35 @@
margin-left: 24px;
padding: 11px;
width: 600px;
+ margin-top: 32px;
+}
+
+.manageMobileMessagingSettings {
+ .form-group.row .row {
+ margin: 0;
+ }
+}
+
+.addPhoneNumber {
+
+ .countryCode {
+ width:120px;
+ height:80px;
+ position: relative;
+
+ .countryCodeSymbol {
+ position: absolute;
+ top: 32px;
+ left: -4px;
+ }
+ }
+ .phoneNumber {
+ width:180px;
+ height:80px;
+ }
+ .addNumber {
+ width:90px;
+ height:80px;
+ }
+
} \ No newline at end of file
diff --git a/plugins/MobileMessaging/templates/index.twig b/plugins/MobileMessaging/templates/index.twig
index 5a405a462e..c0692c7795 100644
--- a/plugins/MobileMessaging/templates/index.twig
+++ b/plugins/MobileMessaging/templates/index.twig
@@ -5,61 +5,168 @@
{% set title %}{{ 'MobileMessaging_SettingsMenu'|translate }}{% endset %}
{% block content %}
-
+<div class="manageMobileMessagingSettings">
{% if isSuperUser %}
- <h2>{{ title }}</h2>
-
- <div class="form-group">
- <label>{{ 'MobileMessaging_Settings_LetUsersManageAPICredential'|translate }}</label>
-
- <label class="radio">
- <input type='radio' value='false' id="delegatedManagement" name='delegatedManagement'
- {% if not delegatedManagement %} checked='checked'{% endif %}/>
- {{ 'General_No'|translate }}
-
- <span class='form-description'>{{ 'General_Default'|translate }}.
- {{ 'MobileMessaging_Settings_LetUsersManageAPICredential_No_Help'|translate }}</span>
- </label>
-
- <label class="radio">
- <input type='radio' value='true' id="delegatedManagement" name='delegatedManagement'
- {% if delegatedManagement %} checked='checked'{% endif %}/>
- {{ 'General_Yes'|translate }}
-
- <span class='form-description'>{{ 'MobileMessaging_Settings_LetUsersManageAPICredential_Yes_Help'|translate }}</span>
- </label>
+ <div piwik-content-block content-title="{{ title|e('html_attr') }}">
+ <div ng-controller="DelegateMobileMessagingSettingsController as delegateManagement">
+ <div piwik-field uicontrol="radio" name="delegatedManagement"
+ options="{{ delegateManagementOptions|json_encode }}"
+ full-width="true"
+ ng-model="delegateManagement.enabled"
+ title="{{ 'MobileMessaging_Settings_LetUsersManageAPICredential'|translate|e('html_attr') }}"
+ value="{% if delegatedManagement %}1{% else %}0{% endif %}">
+ </div>
+ <div piwik-save-button onconfirm="delegateManagement.save()" saving="delegateManagement.isLoading"></div>
</div>
+ </div>
{% endif %}
- {% if accountManagedByCurrentUser and delegatedManagement %}
+ {% if accountManagedByCurrentUser %}
+ <div piwik-content-block content-title="{{ 'MobileMessaging_Settings_SMSProvider'|translate|e('html_attr') }}" feature="true">
- <h2 piwik-enriched-headline
- >{{ 'MobileMessaging_Settings_SMSProvider'|translate }}</h2>
- To manage your SMS provider go to your <a href="{{ linkTo({'action':'userSettings'}) }}">personal mobile messaging settings</a>.
+ {% if isSuperUser and delegatedManagement %}
+ <p>{{ 'MobileMessaging_Settings_DelegatedSmsProviderOnlyAppliesToYou'|translate }}</p>
+ {% endif %}
- {% elseif accountManagedByCurrentUser %}
-
- <h2 piwik-enriched-headline
- >{{ 'MobileMessaging_Settings_SMSProvider'|translate }}</h2>
-
- {{ macro.manageSmsApi(credentialSupplied, creditLeft, smsProviders, provider) }}
+ {{ macro.manageSmsApi(credentialSupplied, creditLeft, smsProviderOptions, smsProviders, provider) }}
+ </div>
{% endif %}
- {% import 'ajaxMacros.twig' as ajax %}
-
- <div style="margin-top:10px">
- {{ ajax.errorDiv('ajaxErrorMobileMessagingSettings') }}
+ <div piwik-content-block content-title="{{ 'MobileMessaging_PhoneNumbers'|translate|e('html_attr') }}">
+ {% if not credentialSupplied %}
+ <p>
+ {% if accountManagedByCurrentUser %}
+ {{ 'MobileMessaging_Settings_CredentialNotProvided'|translate }}
+ {% else %}
+ {{ 'MobileMessaging_Settings_CredentialNotProvidedByAdmin'|translate }}
+ {% endif %}
+ </p>
+ {% else %}
+ <div ng-controller="ManageMobilePhoneNumbersController as managePhoneNumber">
+
+ <p>{{ 'MobileMessaging_Settings_PhoneNumbers_Help'|translate }}</p>
+
+ {% if isSuperUser %}
+ <p>{{ 'MobileMessaging_Settings_DelegatedPhoneNumbersOnlyUsedByYou'|translate }}</p>
+ {% endif %}
+
+ <div class="row">
+ <h3 class="col s12">{{ 'MobileMessaging_Settings_PhoneNumbers_Add'|translate }}</h3>
+ </div>
+
+ <div class="form-group row">
+ <div class="col s12 m6">
+ <div piwik-field uicontrol="select" name="countryCodeSelect"
+ value="{{ defaultCallingCode }}"
+ ng-model="managePhoneNumber.countryCallingCode"
+ full-width="true"
+ title="{{ 'MobileMessaging_Settings_SelectCountry'|translate|e('html_attr') }}"
+ options='{{ countries|json_encode }}'>
+ </div>
+ </div>
+ <div class="col s12 m6 form-help">
+ {{ 'MobileMessaging_Settings_PhoneNumbers_CountryCode_Help'|translate }}
+ </div>
+ </div>
+
+ <div class="form-group row addPhoneNumber">
+ <div class="col s12 m6">
+
+ <div class="countryCode left">
+ <span class="countryCodeSymbol">+</span>
+ <div piwik-field uicontrol="text" name="countryCallingCode"
+ full-width="true"
+ ng-model="managePhoneNumber.countryCallingCode"
+ maxlength="4"
+ title="{{ 'MobileMessaging_Settings_CountryCode'|translate }}">
+ </div>
+ </div>
+ <div class="phoneNumber left">
+ <div piwik-field uicontrol="text" name="newPhoneNumber"
+ ng-model="managePhoneNumber.newPhoneNumber"
+ ng-change="managePhoneNumber.validateNewPhoneNumberFormat()"
+ full-width="true"
+ maxlength="80"
+ title="{{ 'MobileMessaging_Settings_PhoneNumber'|translate }}">
+ </div>
+ </div>
+ <div class="addNumber left valign-wrapper">
+ <div piwik-save-button
+ disabled="!managePhoneNumber.canAddNumber || managePhoneNumber.isAddingPhonenumber"
+ onconfirm="managePhoneNumber.addPhoneNumber()"
+ class="valign" value='{{ 'General_Add'|translate }}'></div>
+ </div>
+
+ <div piwik-alert="warning"
+ id="suspiciousPhoneNumber"
+ ng-show="managePhoneNumber.showSuspiciousPhoneNumber">
+ {{ 'MobileMessaging_Settings_SuspiciousPhoneNumber'|translate('54184032') }}
+ </div>
+
+ </div>
+ <div class="col s12 m6 form-help">
+ {{ strHelpAddPhone }}
+ </div>
+ </div>
+
+ <div id="ajaxErrorAddPhoneNumber"></div>
+ <div piwik-activity-indicator loading="managePhoneNumber.isAddingPhonenumber"></div>
+
+ {% if phoneNumbers|length > 0 %}
+ <div class="row"><h3 class="col s12">{{ 'MobileMessaging_Settings_ManagePhoneNumbers'|translate }}</h3></div>
+ {% endif %}
+
+ {% for phoneNumber, validated in phoneNumbers %}
+ <div class="form-group row">
+ <div class="col s12 m6">
+ <span class='phoneNumber'>{{ phoneNumber }}</span>
+
+ {% if not validated %}
+ <input type="text"
+ ng-hide="managePhoneNumber.isActivated[{{ loop.index }}]"
+ ng-model="managePhoneNumber.validationCode[{{ loop.index }}]"
+ class='verificationCode'
+ placeholder="{{ 'MobileMessaging_Settings_EnterActivationCode'|translate|e('html_attr') }}"/>
+ <div piwik-save-button
+ ng-hide="managePhoneNumber.isActivated[{{ loop.index }}]"
+ value='{{ 'MobileMessaging_Settings_ValidatePhoneNumber'|translate }}'
+ disabled="!managePhoneNumber.validationCode[{{ loop.index }}] || managePhoneNumber.isChangingPhoneNumber"
+ onconfirm='managePhoneNumber.validateActivationCode({{ phoneNumber|json_encode }}, {{ loop.index }})'
+ ></div>
+ {% endif %}
+
+ <div piwik-save-button
+ value='{{ 'General_Remove'|translate }}'
+ disabled="managePhoneNumber.isChangingPhoneNumber"
+ onconfirm="managePhoneNumber.removePhoneNumber({{ phoneNumber|json_encode }})"
+ ></div>
+ </div>
+
+ {% if not validated %}
+ <div class="form-help col s12 m6">
+ <div ng-hide="managePhoneNumber.isActivated[{{ loop.index }}]">
+ {{ 'MobileMessaging_Settings_VerificationCodeJustSent'|translate }}
+ </div>
+ &nbsp;
+ </div>
+ {% endif %}
+ </div>
+ {% endfor %}
+
+ {{ ajax.errorDiv('invalidVerificationCodeAjaxError') }}
+
+ <div piwik-activity-indicator loading="managePhoneNumber.isChangingPhoneNumber"></div>
+
+ </div>
+
+ {% endif %}
</div>
- <h2>{{ 'MobileMessaging_PhoneNumbers'|translate }}</h2>
- To manage your phone numbers go to your <a href="{{ linkTo({'action':'userSettings'}) }}">personal mobile messaging settings</a>.
-
- {{ ajax.loadingDiv('ajaxLoadingMobileMessagingSettings') }}
<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 }}'/>
</div>
-
+</div>
{% endblock %}
diff --git a/plugins/MobileMessaging/templates/macros.twig b/plugins/MobileMessaging/templates/macros.twig
index e0c1d499c6..9cb4c93e6d 100644
--- a/plugins/MobileMessaging/templates/macros.twig
+++ b/plugins/MobileMessaging/templates/macros.twig
@@ -1,33 +1,79 @@
-{% macro manageSmsApi(credentialSupplied, creditLeft, smsProviders, provider) %}
+{% macro manageSmsApi(credentialSupplied, creditLeft, smsProviderOptions, smsProviders, provider) %}
+<div ng-controller="ManageSmsProviderController as manageProvider">
+
+ <div piwik-activity-indicator loading="manageProvider.isDeletingAccount"></div>
+ <div id="ajaxErrorManageSmsProviderSettings"></div>
+
{% if credentialSupplied %}
- {{ 'MobileMessaging_Settings_CredentialProvided'|translate(provider) }}
- {{ creditLeft }}
- <br/>
- {{ 'MobileMessaging_Settings_UpdateOrDeleteAccount'|translate("<a id='displayAccountForm'>","</a>","<a id='deleteAccount'>","</a>")|raw }}
+ <p>
+ {{ 'MobileMessaging_Settings_CredentialProvided'|translate(provider) }}
+ {{ creditLeft }}
+ <br/>
+ {{ 'MobileMessaging_Settings_UpdateOrDeleteAccount'|translate('<a ng-click="manageProvider.showUpdateAccount()" id="displayAccountForm">',"</a>",'<a ng-click="manageProvider.deleteAccount()" id="deleteAccount">',"</a>")|raw }}
+ </p>
{% else %}
- {{ 'MobileMessaging_Settings_PleaseSignUp'|translate }}
+ <p>{{ 'MobileMessaging_Settings_PleaseSignUp'|translate }}</p>
{% endif %}
- <div id='accountForm' {% if credentialSupplied %}style='display: none;'{% endif %}>
- <br/>
- {{ 'MobileMessaging_Settings_SMSProvider'|translate }}
- <select id='smsProviders'>
- {% for smsProvider, description in smsProviders %}
- <option value='{{ smsProvider }}'>
- {{ smsProvider }}
- </option>
- {% endfor %}
- </select>
-
- {{ 'MobileMessaging_Settings_APIKey'|translate }}
- <input size='25' id='apiKey'/>
-
- <input type='submit' value='{{ 'General_Save'|translate }}' id='apiAccountSubmit' class='submit'/>
+
+ <div piwik-form id='accountForm' {% if credentialSupplied %}ng-show="manageProvider.showAccountForm"{% endif %}>
+
+ <div piwik-field uicontrol="select" name="smsProviders"
+ options="{{ smsProviderOptions|json_encode }}"
+ ng-model="manageProvider.smsProvider"
+ ng-change="manageProvider.isUpdateAccountPossible()"
+ title="{{ 'MobileMessaging_Settings_SMSProvider'|translate|e('html_attr') }}"
+ value="{{ provider }}">
+ </div>
+
+ <div piwik-field uicontrol="text" name="apiKey"
+ ng-model="manageProvider.apiKey"
+ required="true"
+ ng-change="manageProvider.isUpdateAccountPossible()"
+ title="{{ 'MobileMessaging_Settings_APIKey'|translate|e('html_attr') }}"
+ value="">
+ </div>
+
+ <div piwik-save-button id='apiAccountSubmit'
+ disabled="!manageProvider.canBeUpdated"
+ saving="manageProvider.isUpdatingAccount"
+ onconfirm="manageProvider.updateAccount()"></div>
{% for smsProvider, description in smsProviders %}
- <div class='providerDescription' id='{{ smsProvider }}'>
+ <div class='providerDescription'
+ ng-show="manageProvider.smsProvider == '{{ smsProvider|e('js') }}'"
+ id='{{ smsProvider }}'>
{{ description|raw }}
</div>
{% endfor %}
</div>
+</div>
+{% endmacro %}
+
+{% macro selectPhoneNumbers(phoneNumbers, angularContext, value, withIntroduction) %}
+ <div id="mobilePhoneNumbersHelp" class="inline-help-node">
+ <span class="icon-info"></span>
+
+ {% if phoneNumbers|length == 0 %}
+ {{ 'MobileMessaging_MobileReport_NoPhoneNumbers'|translate }}
+ {% else %}
+ {{ 'MobileMessaging_MobileReport_AdditionalPhoneNumbers'|translate|e('html_attr') }}
+ {% endif %}
+ <a href="{{ linkTo({'module':"MobileMessaging", 'action': 'index', 'updated':null}) }}">{{ 'MobileMessaging_MobileReport_MobileMessagingSettingsLink'|translate }}</a>
+ </div>
+
+ <div class='mobile'
+ piwik-field uicontrol="checkbox"
+ var-type="array"
+ name="phoneNumbers"
+ ng-model="{{ angularContext }}.report.phoneNumbers"
+ {% if withIntroduction %}
+ introduction="{{ 'ScheduledReports_SendReportTo'|translate|e('html_attr') }}"
+ {% endif %}
+ title="{{ 'MobileMessaging_PhoneNumbers'|translate|e('html_attr') }}"
+ {% if phoneNumbers|length == 0 %}disabled="true"{% endif %}
+ options="{{ phoneNumbers|json_encode }}"
+ inline-help="#mobilePhoneNumbersHelp"
+ {% if value %}value="{{ value|json_encode }}"{% endif %}>
+ </div>
{% endmacro %} \ No newline at end of file
diff --git a/plugins/MobileMessaging/templates/reportParametersScheduledReports.twig b/plugins/MobileMessaging/templates/reportParametersScheduledReports.twig
index cddee36095..5ca96fa9fb 100644
--- a/plugins/MobileMessaging/templates/reportParametersScheduledReports.twig
+++ b/plugins/MobileMessaging/templates/reportParametersScheduledReports.twig
@@ -1,62 +1,30 @@
-
-<tr class='{{ reportType }}'>
- <td class="first">
- {{ 'MobileMessaging_PhoneNumbers'|translate }}
- </td>
- <td>
- <div class="entityInlineHelp">
- {% if phoneNumbers|length == 0 %}
- <div><span class="icon-info"></span> {{ 'MobileMessaging_MobileReport_NoPhoneNumbers'|translate }}
- {% else %}
- <ul class="clearfix">
- {% for phoneNumber in phoneNumbers %}
- <li class="clear"><label><input name='phoneNumbers' type='checkbox' id='{{ phoneNumber }}'/>{{ phoneNumber }}</label></li>
- {% endfor %}
- </ul>
- <div><span class="icon-info"></span> {{ 'MobileMessaging_MobileReport_AdditionalPhoneNumbers'|translate }}
- {% endif %}
- <a href="{{ linkTo({'module':"MobileMessaging", 'action': 'index', 'updated':null}) }}">{{ 'MobileMessaging_MobileReport_MobileMessagingSettingsLink'|translate }}</a></div>
- </div>
- <script>
- $(function () {
- resetReportParametersFunctions ['{{ reportType }}'] = function () {
- var reportParameters = {
- 'phoneNumbers': []
- };
- updateReportParametersFunctions['{{ reportType }}'](reportParameters);
- };
-
- updateReportParametersFunctions['{{ reportType }}'] = function (reportParameters) {
-
- if (reportParameters == null) return;
-
- $('[name=phoneNumbers]').removeProp('checked');
- $(reportParameters.phoneNumbers).each(function (index, phoneNumber) {
- $('#\\' + phoneNumber).prop('checked', 'checked');
- });
-
- $(document).trigger('ScheduledReport.edit', {});
-
- };
-
- getReportParametersFunctions['{{ reportType }}'] = function () {
- var parameters = Object();
- var selectedPhoneNumbers = $.map(
- $('[name=phoneNumbers]').filter(function() {
- return !this.disabled && this.checked;
- }),
- function (phoneNumber) {
- return $(phoneNumber).attr('id');
- }
- );
-
- // 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;
- };
- });
- </script>
-
- </td>
-</tr>
+{% import '@MobileMessaging/macros.twig' as macro %}
+
+<div ng-show="manageScheduledReport.report.type == 'mobile'">
+ {{ macro.selectPhoneNumbers(phoneNumbers, 'manageScheduledReport', '', true) }}
+</div>
+
+<script>
+$(function () {
+ resetReportParametersFunctions['mobile'] = function (report) {
+ report.phoneNumbers = [];
+ report.formatmobile = 'sms';
+ };
+
+ updateReportParametersFunctions['mobile'] = function (report) {
+ if (report.parameters && report.parameters.phoneNumbers) {
+ report.phoneNumbers = report.parameters.phoneNumbers;
+ }
+ report.formatmobile = 'sms';
+ };
+
+ getReportParametersFunctions['mobile'] = function (report) {
+ var parameters = {};
+
+ // returning [''] when no phone numbers are selected avoids the "please provide a value for 'parameters'" error message
+ parameters.phoneNumbers = report.phoneNumbers && report.phoneNumbers.length > 0 ? report.phoneNumbers : [''];
+
+ return parameters;
+ };
+});
+</script> \ No newline at end of file
diff --git a/plugins/MobileMessaging/templates/userSettings.twig b/plugins/MobileMessaging/templates/userSettings.twig
deleted file mode 100644
index 0bb2200979..0000000000
--- a/plugins/MobileMessaging/templates/userSettings.twig
+++ /dev/null
@@ -1,129 +0,0 @@
-{% extends 'admin.twig' %}
-
-{% set title %}{{ 'MobileMessaging_Settings_SMSProvider'|translate }}{% endset %}
-
-{% block content %}
-
- {% import 'ajaxMacros.twig' as ajax %}
-
- <div style="margin-top:10px">
- {{ ajax.errorDiv('ajaxErrorMobileMessagingSettings') }}
- </div>
-
- {% import '@MobileMessaging/macros.twig' as macro %}
-
- {% if accountManagedByCurrentUser and delegatedManagement %}
- <h2 piwik-enriched-headline>{{ title }}</h2>
-
- {{ macro.manageSmsApi(credentialSupplied, creditLeft, smsProviders, provider) }}
- {% endif %}
-
- <h2>{{ 'MobileMessaging_PhoneNumbers'|translate }}</h2>
- {% if not credentialSupplied %}
- {% if accountManagedByCurrentUser and delegatedManagement %}
- {{ 'MobileMessaging_Settings_CredentialNotProvided'|translate }}
- {% elseif accountManagedByCurrentUser %}
- Before you can create and manage phone numbers, please setup an SMS provider in <a href="{{ linkTo({'action': 'index'}) }}">admin mobile messaging settings</a>.
- {% else %}
- {{ 'MobileMessaging_Settings_CredentialNotProvidedByAdmin'|translate }}
- {% endif %}
- {% else %}
- {{ 'MobileMessaging_Settings_PhoneNumbers_Help'|translate }}
- <span class="form-group">
- <div class="form-help">
- {{ strHelpAddPhone }}
- </div>
- <span>
- <p><strong>{{ 'MobileMessaging_Settings_PhoneNumbers_Add'|translate }}</strong></p>
- </span>
- <span id="suspiciousPhoneNumber" style="display:none;">
- {{ 'MobileMessaging_Settings_SuspiciousPhoneNumber'|translate('54184032') }}
- <br/><br/>
- </span>
-
- </span>
-
- + <input id="countryCallingCode" size="4" maxlength="4"/>&nbsp;
- <input id="newPhoneNumber"/>
- <input type="submit" value='{{ 'General_Add'|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">
- {# this is a trick to avoid selecting the first country when no default could be found #}
- <option value="">&nbsp;</option>
- {% for countryCode, country in countries %}
- <option value='{{ country.countryCallingCode }}'
- {% if defaultCountry==countryCode %} selected="selected" {% endif %}
- >
- {{ country.countryName }}
- </option>
- {% endfor %}
- </select>
-
-
- {% if phoneNumbers|length > 0 %}
- <br/>
- <br/>
- <strong>{{ 'MobileMessaging_Settings_ManagePhoneNumbers'|translate }}</strong>
- <br/>
- <br/>
- {% endif %}
-
- {{ ajax.errorDiv('invalidVerificationCodeAjaxError') }}
-
- <div id='phoneNumberActivated' style="display:none;">
- {{ 'MobileMessaging_Settings_PhoneActivated'|translate }}
- </div>
-
- <div id='invalidActivationCode' style="display:none;">
- {{ 'MobileMessaging_Settings_InvalidActivationCode'|translate }}
- </div>
-
- <ul>
- {% for phoneNumber, validated in phoneNumbers %}
- <li>
- <span class='phoneNumber'>{{ phoneNumber }}</span>
- {% if not validated %}
- <input class='verificationCode'/>
- <input
- type='submit'
- value='{{ 'MobileMessaging_Settings_ValidatePhoneNumber'|translate }}'
- class='validatePhoneNumberSubmit'
- />
- {% endif %}
- <input
- type='submit'
- value='{{ 'General_Remove'|translate }}'
- class='removePhoneNumberSubmit'
- />
- {% if not validated %}
- <br/>
- <span class='form-description'>{{ 'MobileMessaging_Settings_VerificationCodeJustSent'|translate }}</span>
- {% endif %}
- <br/>
- <br/>
- </li>
- {% endfor %}
- </ul>
-
- {% endif %}
-
- {{ ajax.loadingDiv('ajaxLoadingMobileMessagingSettings') }}
-
- <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 }}'/>
- </div>
-
-{% endblock %}
diff --git a/plugins/MobileMessaging/tests/Integration/MobileMessagingTest.php b/plugins/MobileMessaging/tests/Integration/MobileMessagingTest.php
index 9b03f74fa3..64a5f80435 100644
--- a/plugins/MobileMessaging/tests/Integration/MobileMessagingTest.php
+++ b/plugins/MobileMessaging/tests/Integration/MobileMessagingTest.php
@@ -241,7 +241,10 @@ class MobileMessagingTest extends IntegrationTestCase
'parameters' => array(MobileMessaging::PHONE_NUMBERS_PARAMETER => array($phoneNumber)),
);
- $stubbedAPIMobileMessaging = $this->getMock('\\Piwik\\Plugins\\MobileMessaging\\API', array('sendSMS', 'getInstance'), $arguments = array(), $mockClassName = '', $callOriginalConstructor = false);
+ $stubbedAPIMobileMessaging = $this->getMockBuilder('\\Piwik\\Plugins\\MobileMessaging\\API')
+ ->setMethods(array('sendSMS', 'getInstance'))
+ ->disableOriginalConstructor()
+ ->getMock();
$stubbedAPIMobileMessaging->expects($this->once())->method('sendSMS')->with(
$this->equalTo($expectedReportContent, 0),
$this->equalTo($expectedPhoneNumber, 1),
diff --git a/plugins/Morpheus/Menu.php b/plugins/Morpheus/Menu.php
index a45b35c0b6..1b10d1ddb5 100644
--- a/plugins/Morpheus/Menu.php
+++ b/plugins/Morpheus/Menu.php
@@ -22,9 +22,14 @@ class Menu extends \Piwik\Plugin\Menu
$menu->registerMenuIcon('General_Settings', 'icon-admin-settings');
$menu->registerMenuIcon('CoreAdminHome_Administration', 'icon-admin-administration');
$menu->registerMenuIcon('UsersManager_MenuPersonal', 'icon-user-personal');
- $menu->registerMenuIcon('CoreAdminHome_MenuManage', 'icon-user-manage');
+ $menu->registerMenuIcon('CoreAdminHome_MenuSystem', 'icon-server');
$menu->registerMenuIcon('CorePluginsAdmin_MenuPlatform', 'icon-user-platform');
+ $manageMeasurablesIcon = 'icon-open-source';
+ $menu->registerMenuIcon('CoreAdminHome_MenuMeasurables', $manageMeasurablesIcon);
+ $menu->registerMenuIcon('SitesManager_Sites', $manageMeasurablesIcon);
+ $menu->registerMenuIcon('MobileAppMeasurable_MobileApps', $manageMeasurablesIcon);
+
if (Development::isEnabled() && Piwik::isUserHasSomeAdminAccess()) {
$menu->addDevelopmentItem('UI Demo', $this->urlForAction('demo'));
}
diff --git a/plugins/Morpheus/images/logo.svg b/plugins/Morpheus/images/logo.svg
index 8c0be81ce9..7beb6ee54d 100644
--- a/plugins/Morpheus/images/logo.svg
+++ b/plugins/Morpheus/images/logo.svg
@@ -3,22 +3,22 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="170.666px" height="45.704px" viewBox="0 0 170.666 45.704" enable-background="new 0 0 170.666 45.704"
xml:space="preserve">
-<path fill="#d4291f" d="M106.995,1.016c-2.57-0.739-5.251,0.751-5.987,3.329l-5.532,19.351L89.943,4.345
+<path fill="#ffffff" d="M106.995,1.016c-2.57-0.739-5.251,0.751-5.987,3.329l-5.532,19.351L89.943,4.345
c-0.592-2.071-2.441-3.417-4.477-3.492c-0.103-0.009-0.203-0.004-0.306-0.007c-0.102,0.003-0.203-0.003-0.306,0.007
c-2.036,0.075-3.885,1.421-4.477,3.492l-5.509,19.27l-5.509-19.27c-0.737-2.578-3.418-4.068-5.987-3.329
c-2.57,0.739-4.056,3.428-3.319,6.005l10.075,35.24c0.583,2.038,2.384,3.376,4.382,3.49c0.121,0.011,0.239,0.006,0.359,0.008
c0.12-0.002,0.238,0.004,0.359-0.008c1.998-0.114,3.799-1.451,4.382-3.49l5.551-19.416l5.551,19.416
c0.588,2.056,2.416,3.398,4.434,3.491c0.111,0.01,0.219,0.005,0.33,0.007c0.11-0.003,0.219,0.003,0.33-0.007
c2.019-0.093,3.846-1.435,4.434-3.491l10.075-35.24C111.05,4.444,109.564,1.756,106.995,1.016z"/>
-<path fill="#d4291f" d="M116.502,40.44c0,2.972,2.35,5.322,5.322,5.322c2.972,0,5.322-2.35,5.322-5.322V6.147
+<path fill="#ffffff" d="M116.502,40.44c0,2.972,2.35,5.322,5.322,5.322c2.972,0,5.322-2.35,5.322-5.322V6.147
c0-2.972-2.35-5.322-5.322-5.322c-2.972,0-5.322,2.35-5.322,5.322V40.44z"/>
-<path fill="#d4291f" d="M43.334,40.457c0,2.972,2.35,5.322,5.322,5.322c2.972,0,5.322-2.35,5.322-5.322V6.164
+<path fill="#ffffff" d="M43.334,40.457c0,2.972,2.35,5.322,5.322,5.322c2.972,0,5.322-2.35,5.322-5.322V6.164
c0-2.972-2.35-5.322-5.322-5.322c-2.972,0-5.322,2.35-5.322,5.322V40.457z"/>
-<path fill="#d4291f" d="M155.406,23.156l13.779-13.779c1.974-1.974,1.974-5.097,0-7.071c-1.974-1.974-5.097-1.974-7.071,0
+<path fill="#ffffff" d="M155.406,23.156l13.779-13.779c1.974-1.974,1.974-5.097,0-7.071c-1.974-1.974-5.097-1.974-7.071,0
l-16.448,16.448V6.147c0-2.972-2.35-5.322-5.322-5.322c-2.972,0-5.322,2.35-5.322,5.322V40.44c0,2.972,2.35,5.322,5.322,5.322
c2.972,0,5.322-2.35,5.322-5.322v-7.544l2.669-2.669l13.618,13.857c1.957,1.992,5.079,2.019,7.071,0.062
c1.992-1.957,2.019-5.079,0.062-7.071L155.406,23.156z"/>
-<path fill="#d4291f" d="M37.292,16.816c0-8.58-6.787-15.557-15.283-15.9l0.007-0.033H5.322C2.35,0.883,0,3.233,0,6.205v34.177
+<path fill="#ffffff" d="M37.292,16.816c0-8.58-6.787-15.557-15.283-15.9l0.007-0.033H5.322C2.35,0.883,0,3.233,0,6.205v34.177
c0,2.972,2.35,5.322,5.322,5.322c2.972,0,5.322-2.35,5.322-5.322v-7.63h11.371l-0.007-0.036
C30.505,32.372,37.292,25.396,37.292,16.816z M20.296,23.284l0-0.002h-9.651V10.491h9.464c3.535,0,6.401,2.866,6.401,6.401
C26.51,20.364,23.744,23.184,20.296,23.284z"/>
diff --git a/plugins/Morpheus/javascripts/ajaxHelper.js b/plugins/Morpheus/javascripts/ajaxHelper.js
index 0f0e60259d..c46984e5ce 100644
--- a/plugins/Morpheus/javascripts/ajaxHelper.js
+++ b/plugins/Morpheus/javascripts/ajaxHelper.js
@@ -439,7 +439,8 @@ function ajaxHelper() {
};
this._isRequestToApiMethod = function () {
- return this.getParams && this.getParams['module'] === 'API' && this.getParams['method'];
+ return (this.getParams && this.getParams['module'] === 'API' && this.getParams['method']) ||
+ (this.postParams && this.postParams['module'] === 'API' && this.postParams['method']);
};
this._getDefaultPostParams = function () {
diff --git a/plugins/Morpheus/javascripts/jquery.icheck.min.js b/plugins/Morpheus/javascripts/jquery.icheck.min.js
deleted file mode 100644
index 2ca9f12eb8..0000000000
--- a/plugins/Morpheus/javascripts/jquery.icheck.min.js
+++ /dev/null
@@ -1,8 +0,0 @@
-(function(f){function C(a,c,d){var b=a[0],e=/er/.test(d)?k:/bl/.test(d)?u:j;active=d==E?{checked:b[j],disabled:b[u],indeterminate:"true"==a.attr(k)||"false"==a.attr(v)}:b[e];if(/^(ch|di|in)/.test(d)&&!active)p(a,e);else if(/^(un|en|de)/.test(d)&&active)w(a,e);else if(d==E)for(var e in active)active[e]?p(a,e,!0):w(a,e,!0);else if(!c||"toggle"==d){if(!c)a[r]("ifClicked");active?b[l]!==x&&w(a,e):p(a,e)}}function p(a,c,d){var b=a[0],e=a.parent(),g=c==j,H=c==k,m=H?v:g?I:"enabled",r=h(b,m+y(b[l])),L=h(b,
-c+y(b[l]));if(!0!==b[c]){if(!d&&c==j&&b[l]==x&&b.name){var p=a.closest("form"),s='input[name="'+b.name+'"]',s=p.length?p.find(s):f(s);s.each(function(){this!==b&&f.data(this,n)&&w(f(this),c)})}H?(b[c]=!0,b[j]&&w(a,j,"force")):(d||(b[c]=!0),g&&b[k]&&w(a,k,!1));J(a,g,c,d)}b[u]&&h(b,z,!0)&&e.find("."+F).css(z,"default");e[t](L||h(b,c));e[A](r||h(b,m)||"")}function w(a,c,d){var b=a[0],e=a.parent(),g=c==j,f=c==k,m=f?v:g?I:"enabled",n=h(b,m+y(b[l])),p=h(b,c+y(b[l]));if(!1!==b[c]){if(f||!d||"force"==d)b[c]=
-!1;J(a,g,m,d)}!b[u]&&h(b,z,!0)&&e.find("."+F).css(z,"pointer");e[A](p||h(b,c)||"");e[t](n||h(b,m))}function K(a,c){if(f.data(a,n)){var d=f(a);d.parent().html(d.attr("style",f.data(a,n).s||"")[r](c||""));d.off(".i").unwrap();f(D+'[for="'+a.id+'"]').add(d.closest(D)).off(".i")}}function h(a,c,d){if(f.data(a,n))return f.data(a,n).o[c+(d?"":"Class")]}function y(a){return a.charAt(0).toUpperCase()+a.slice(1)}function J(a,c,d,b){if(!b){if(c)a[r]("ifToggled");a[r]("ifChanged")[r]("if"+y(d))}}var n="iCheck",
-F=n+"-helper",x="radio",j="checked",I="un"+j,u="disabled",v="determinate",k="in"+v,E="update",l="type",t="addClass",A="removeClass",r="trigger",D="label",z="cursor",G=/ipad|iphone|ipod|android|blackberry|windows phone|opera mini/i.test(navigator.userAgent);f.fn[n]=function(a,c){var d=":checkbox, :"+x,b=f(),e=function(a){a.each(function(){var a=f(this);b=a.is(d)?b.add(a):b.add(a.find(d))})};if(/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(a))return a=a.toLowerCase(),
-e(this),b.each(function(){"destroy"==a?K(this,"ifDestroyed"):C(f(this),!0,a);f.isFunction(c)&&c()});if("object"==typeof a||!a){var g=f.extend({checkedClass:j,disabledClass:u,indeterminateClass:k,labelHover:!0},a),h=g.handle,m=g.hoverClass||"hover",y=g.focusClass||"focus",v=g.activeClass||"active",z=!!g.labelHover,s=g.labelHoverClass||"hover",B=(""+g.increaseArea).replace("%","")|0;if("checkbox"==h||h==x)d=":"+h;-50>B&&(B=-50);e(this);return b.each(function(){K(this);var a=f(this),b=this,c=b.id,d=
--B+"%",e=100+2*B+"%",e={position:"absolute",top:d,left:d,display:"block",width:e,height:e,margin:0,padding:0,background:"#fff",border:0,opacity:0},d=G?{position:"absolute",visibility:"hidden"}:B?e:{position:"absolute",opacity:0},h="checkbox"==b[l]?g.checkboxClass||"icheckbox":g.radioClass||"i"+x,k=f(D+'[for="'+c+'"]').add(a.closest(D)),q=a.wrap('<div class="'+h+'"/>')[r]("ifCreated").parent().append(g.insert),e=f('<ins class="'+F+'"/>').css(e).appendTo(q);a.data(n,{o:g,s:a.attr("style")}).css(d);
-g.inheritClass&&q[t](b.className);g.inheritID&&c&&q.attr("id",n+"-"+c);"static"==q.css("position")&&q.css("position","relative");C(a,!0,E);if(k.length)k.on("click.i mouseenter.i mouseleave.i touchbegin.i touchend.i",function(c){var d=c[l],e=f(this);if(!b[u])if("click"==d?C(a,!1,!0):z&&(/ve|nd/.test(d)?(q[A](m),e[A](s)):(q[t](m),e[t](s))),G)c.stopPropagation();else return!1});a.on("click.i focus.i blur.i keyup.i keydown.i keypress.i",function(c){var d=c[l];c=c.keyCode;if("click"==d)return!1;if("keydown"==
-d&&32==c)return b[l]==x&&b[j]||(b[j]?w(a,j):p(a,j)),!1;if("keyup"==d&&b[l]==x)!b[j]&&p(a,j);else if(/us|ur/.test(d))q["blur"==d?A:t](y)});e.on("click mousedown mouseup mouseover mouseout touchbegin.i touchend.i",function(d){var c=d[l],e=/wn|up/.test(c)?v:m;if(!b[u]){if("click"==c)C(a,!1,!0);else{if(/wn|er|in/.test(c))q[t](e);else q[A](e+" "+v);if(k.length&&z&&e==m)k[/ut|nd/.test(c)?A:t](s)}if(G)d.stopPropagation();else return!1}})})}return this}})(jQuery);
diff --git a/plugins/Morpheus/javascripts/layout.js b/plugins/Morpheus/javascripts/layout.js
index c155945ea9..8227b36e0d 100644
--- a/plugins/Morpheus/javascripts/layout.js
+++ b/plugins/Morpheus/javascripts/layout.js
@@ -6,21 +6,12 @@
*/
$(function () {
- var contentUser = $('#content.user');
-
function adjustSize(content)
{
- var width = $('body').width() - content.offset().left - 10;
+ var width = $('body').width() - content.offset().left - 16;
content.css('width', width + 'px');
}
- if (contentUser.length) {
- adjustSize(contentUser);
- $(window).resize(function () {
- adjustSize(contentUser);
- });
- }
-
var contentAdmin = $('#content.admin');
if (contentAdmin.length) {
diff --git a/plugins/Morpheus/javascripts/morpheus.js b/plugins/Morpheus/javascripts/morpheus.js
deleted file mode 100644
index 505a64e32d..0000000000
--- a/plugins/Morpheus/javascripts/morpheus.js
+++ /dev/null
@@ -1,33 +0,0 @@
-$(document).ready(function () {
- // do not apply on the Login page
- if($('#loginPage').length) {
- return;
- }
-
- function initICheck()
- {
- $('input').filter(function () {
- return !$(this).parent().is('.form-radio')
- && !$(this).hasClass('no-icheck');
- }).iCheck({
- checkboxClass: 'form-checkbox',
- radioClass: 'form-radio',
- checkedClass: 'checked',
- hoverClass: 'form-hover'
- });
- }
-
- initICheck();
- $(document).bind('ScheduledReport.edit', initICheck);
- $(document).bind('Goals.edit', initICheck);
- $(broadcast).bind('locationChangeSuccess', initICheck);
- $(broadcast).bind('updateICheck', initICheck);
-
- $('body').on('ifClicked', 'input', function () {
- $(this).trigger('click');
- }).on('ifChanged', 'input', function () {
- if(this.type != 'radio' || this.checked) {
- $(this).trigger('change');
- }
- });
-});
diff --git a/plugins/Morpheus/javascripts/piwikHelper.js b/plugins/Morpheus/javascripts/piwikHelper.js
index 48c1bb8eba..55a5d52c07 100644
--- a/plugins/Morpheus/javascripts/piwikHelper.js
+++ b/plugins/Morpheus/javascripts/piwikHelper.js
@@ -99,6 +99,10 @@ var piwikHelper = {
return url;
},
+ getAngularDependency: function (dependency) {
+ return angular.element(document).injector().get(dependency);
+ },
+
/**
* As we still have a lot of old jQuery code and copy html from node to node we sometimes have to trigger the
* compiling of angular components manually.
@@ -143,6 +147,38 @@ var piwikHelper = {
},
/**
+ * Moves an element further to the left by changing the left margin to make sure as much as possible of an element
+ * is visible in the current viewport. The top position keeps unchanged.
+ * @param elementToPosition
+ */
+ setMarginLeftToBeInViewport: function (elementToPosition) {
+ var availableWidth = $(window).width();
+ $(elementToPosition).css('marginLeft', '0px');
+ var offset = $(elementToPosition).offset();
+ if (!offset) {
+ return;
+ }
+ var leftPos = offset.left;
+ if (leftPos < 0) {
+ leftPos = 0;
+ }
+ var widthSegmentForm = $(elementToPosition).outerWidth();
+ if (leftPos + widthSegmentForm > availableWidth) {
+ var extraSpaceForMoreBeauty = 16;
+ var newLeft = availableWidth - widthSegmentForm - extraSpaceForMoreBeauty;
+ if (newLeft < extraSpaceForMoreBeauty) {
+ newLeft = extraSpaceForMoreBeauty;
+ }
+ var marginLeft = Math.abs(leftPos - newLeft);
+ if (marginLeft > extraSpaceForMoreBeauty) {
+ // we only move it further to the left if it is actually more than 16px to the left.
+ // otherwise it is not really worth it and doesn't look as good.
+ $(elementToPosition).css('marginLeft', (parseInt(marginLeft, 10) * -1) + 'px');
+ }
+ }
+ },
+
+ /**
* Displays a Modal dialog. Text will be taken from the DOM node domSelector.
* Given callback handles will be mapped to the buttons having a role attriute
*
@@ -153,38 +189,53 @@ var piwikHelper = {
* @param {object} handles callback functions for available roles
* @return {void}
*/
- modalConfirm: function( domSelector, handles )
+ modalConfirm: function(domSelector, handles, options)
{
+ if (!options) {
+ options = {};
+ }
+
var domElem = $(domSelector);
var buttons = [];
- $('[role]', domElem).each(function(){
- var role = $(this).attr('role');
- var title = $(this).attr('title');
- var text = $(this).val();
+ var content = '<div class="modal"><div class="modal-content"></div>';
+ content += '<div class="modal-footer"></div></div>';
- var button = {text: text};
+ var $content = $(content).hide();
+ var $footer = $content.find('.modal-footer');
- if(typeof handles[role] == 'function') {
- button.click = function(){ $(this).dialog("close"); handles[role].apply()};
- } else {
- button.click = function(){ $(this).dialog("close");};
+ $('[role]', domElem).each(function(){
+ var $button = $(this);
+ var role = $button.attr('role');
+ var title = $button.attr('title');
+ var text = $button.val();
+ $button.hide();
+
+ var button = $('<a href="javascript:;" class="modal-action modal-close waves-effect waves-light btn-flat "></a>');
+ button.text(text);
+ if (title) {
+ button.attr('title', title);
}
- if (title) {
- button.title = title;
+ if(typeof handles[role] == 'function') {
+ button.on('click', function(){
+ handles[role].apply()
+ });
}
- buttons.push(button);
- $(this).hide();
- });
- domElem.dialog({
- resizable: false,
- modal: true,
- buttons: buttons,
- width: 650,
- position: ['center', 90]
+ $footer.append(button);
});
+
+ $('body').append($content);
+ $content.find('.modal-content').append(domElem);
+
+ if (options && options.fixedFooter) {
+ $content.addClass('modal-fixed-footer');
+ delete options.fixedFooter;
+ }
+
+ domElem.show();
+ $content.openModal(options);
},
getQueryStringWithParametersModified: function (queryString, newParameters) {
@@ -364,6 +415,10 @@ var piwikHelper = {
window.location = url;
},
+ lazyScrollToContent: function () {
+ this.lazyScrollTo('#content', 250);
+ },
+
/**
* Scrolls the window to the jquery element 'elem'
* if the top of the element is not currently visible on screen
@@ -374,7 +429,12 @@ var piwikHelper = {
*/
lazyScrollTo: function(elem, time, forceScroll)
{
- var elemTop = $(elem).offset().top;
+ var $elem = $(elem);
+ if (!$elem.size()) {
+ return;
+ }
+
+ var elemTop = $elem.offset().top;
// only scroll the page if the graph is not visible
if (elemTop < $(window).scrollTop()
|| elemTop > $(window).scrollTop()+$(window).height()
@@ -414,6 +474,16 @@ function isEnterKey(e)
return (window.event?window.event.keyCode:e.which)==13;
}
+/**
+ * Returns true if the event keypress passed in parameter is the ESCAPE key
+ * @param {Event} e current window event
+ * @return {boolean}
+ */
+function isEscapeKey(e)
+{
+ return (window.event?window.event.keyCode:e.which)==27;
+}
+
// workarounds
(function($){
try {
diff --git a/plugins/Morpheus/stylesheets/base.less b/plugins/Morpheus/stylesheets/base.less
index bf2bfd1749..cc0615c85d 100644
--- a/plugins/Morpheus/stylesheets/base.less
+++ b/plugins/Morpheus/stylesheets/base.less
@@ -4,8 +4,6 @@
@import "theme";
@import "theme-advanced";
@import "base/mixins";
-@import "base/bootstrap.css";
-@import "base/icons.css";
/* General styles */
@import "general/_default.less";
@@ -25,6 +23,8 @@
@import "../../CoreHome/angularjs/siteselector/siteselector.directive.less";
@import "../../CoreHome/angularjs/menudropdown/menudropdown.directive.less";
+@import "../../CoreHome/angularjs/alert/alert.directive.less";
+@import "../../CoreHome/stylesheets/dataTable/_entityTable.less";
@import "uibase/_periodSelect.less";
@@ -38,9 +38,9 @@
@import "ui/_buttons";
@import "ui/_code";
@import "ui/_tables";
-@import "ui/_alerts";
@import "ui/_list-group";
@import "ui/_progress-bars";
@import "ui/_navs";
@import "ui/_cards";
+@import "ui/_tabs";
@import "ui/_panels";
diff --git a/plugins/Morpheus/stylesheets/base/bootstrap.css b/plugins/Morpheus/stylesheets/base/bootstrap.css
index 11ad4b4be1..b72d3f7555 100755
--- a/plugins/Morpheus/stylesheets/base/bootstrap.css
+++ b/plugins/Morpheus/stylesheets/base/bootstrap.css
@@ -66,10 +66,7 @@ strong {
dfn {
font-style: italic;
}
-h1 {
- font-size: 2em;
- margin: 0.67em 0;
-}
+
mark {
background: #ff0;
color: #000;
@@ -115,62 +112,6 @@ samp {
font-family: monospace, monospace;
font-size: 1em;
}
-button,
-input,
-optgroup,
-select,
-textarea {
- color: inherit;
- font: inherit;
- margin: 0;
-}
-button {
- overflow: visible;
-}
-button,
-select {
- text-transform: none;
-}
-button,
-html input[type="button"],
-input[type="reset"],
-input[type="submit"] {
- -webkit-appearance: button;
- cursor: pointer;
-}
-button[disabled],
-html input[disabled] {
- cursor: default;
-}
-button::-moz-focus-inner,
-input::-moz-focus-inner {
- border: 0;
- padding: 0;
-}
-input {
- line-height: normal;
-}
-input[type="checkbox"],
-input[type="radio"] {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- padding: 0;
-}
-input[type="number"]::-webkit-inner-spin-button,
-input[type="number"]::-webkit-outer-spin-button {
- height: auto;
-}
-input[type="search"] {
- -webkit-appearance: textfield;
- -moz-box-sizing: content-box;
- -webkit-box-sizing: content-box;
- box-sizing: content-box;
-}
-input[type="search"]::-webkit-search-cancel-button,
-input[type="search"]::-webkit-search-decoration {
- -webkit-appearance: none;
-}
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
@@ -216,21 +157,11 @@ body {
color: #333333;
background-color: #ffffff;
}
-input,
-button,
-select,
-textarea {
- font-family: inherit;
- font-size: inherit;
- line-height: inherit;
-}
a {
- color: #337ab7;
text-decoration: none;
}
a:hover,
a:focus {
- color: #23527c;
text-decoration: underline;
}
a:focus {
@@ -320,661 +251,15 @@ hr {
padding-left: 15px;
padding-right: 15px;
}
-.row {
- margin-left: -15px;
- margin-right: -15px;
-}
-.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
- position: relative;
- min-height: 1px;
- padding-left: 15px;
- padding-right: 15px;
-}
-.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
- float: left;
-}
-.col-xs-12 {
- width: 100%;
-}
-.col-xs-11 {
- width: 91.66666667%;
-}
-.col-xs-10 {
- width: 83.33333333%;
-}
-.col-xs-9 {
- width: 75%;
-}
-.col-xs-8 {
- width: 66.66666667%;
-}
-.col-xs-7 {
- width: 58.33333333%;
-}
-.col-xs-6 {
- width: 50%;
-}
-.col-xs-5 {
- width: 41.66666667%;
-}
-.col-xs-4 {
- width: 33.33333333%;
-}
-.col-xs-3 {
- width: 25%;
-}
-.col-xs-2 {
- width: 16.66666667%;
-}
-.col-xs-1 {
- width: 8.33333333%;
-}
-.col-xs-pull-12 {
- right: 100%;
-}
-.col-xs-pull-11 {
- right: 91.66666667%;
-}
-.col-xs-pull-10 {
- right: 83.33333333%;
-}
-.col-xs-pull-9 {
- right: 75%;
-}
-.col-xs-pull-8 {
- right: 66.66666667%;
-}
-.col-xs-pull-7 {
- right: 58.33333333%;
-}
-.col-xs-pull-6 {
- right: 50%;
-}
-.col-xs-pull-5 {
- right: 41.66666667%;
-}
-.col-xs-pull-4 {
- right: 33.33333333%;
-}
-.col-xs-pull-3 {
- right: 25%;
-}
-.col-xs-pull-2 {
- right: 16.66666667%;
-}
-.col-xs-pull-1 {
- right: 8.33333333%;
-}
-.col-xs-pull-0 {
- right: auto;
-}
-.col-xs-push-12 {
- left: 100%;
-}
-.col-xs-push-11 {
- left: 91.66666667%;
-}
-.col-xs-push-10 {
- left: 83.33333333%;
-}
-.col-xs-push-9 {
- left: 75%;
-}
-.col-xs-push-8 {
- left: 66.66666667%;
-}
-.col-xs-push-7 {
- left: 58.33333333%;
-}
-.col-xs-push-6 {
- left: 50%;
-}
-.col-xs-push-5 {
- left: 41.66666667%;
-}
-.col-xs-push-4 {
- left: 33.33333333%;
-}
-.col-xs-push-3 {
- left: 25%;
-}
-.col-xs-push-2 {
- left: 16.66666667%;
-}
-.col-xs-push-1 {
- left: 8.33333333%;
-}
-.col-xs-push-0 {
- left: auto;
-}
-.col-xs-offset-12 {
- margin-left: 100%;
-}
-.col-xs-offset-11 {
- margin-left: 91.66666667%;
-}
-.col-xs-offset-10 {
- margin-left: 83.33333333%;
-}
-.col-xs-offset-9 {
- margin-left: 75%;
-}
-.col-xs-offset-8 {
- margin-left: 66.66666667%;
-}
-.col-xs-offset-7 {
- margin-left: 58.33333333%;
-}
-.col-xs-offset-6 {
- margin-left: 50%;
-}
-.col-xs-offset-5 {
- margin-left: 41.66666667%;
-}
-.col-xs-offset-4 {
- margin-left: 33.33333333%;
-}
-.col-xs-offset-3 {
- margin-left: 25%;
-}
-.col-xs-offset-2 {
- margin-left: 16.66666667%;
-}
-.col-xs-offset-1 {
- margin-left: 8.33333333%;
-}
-.col-xs-offset-0 {
- margin-left: 0%;
-}
-@media (min-width: 768px) {
- .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
- float: left;
- }
- .col-sm-12 {
- width: 100%;
- }
- .col-sm-11 {
- width: 91.66666667%;
- }
- .col-sm-10 {
- width: 83.33333333%;
- }
- .col-sm-9 {
- width: 75%;
- }
- .col-sm-8 {
- width: 66.66666667%;
- }
- .col-sm-7 {
- width: 58.33333333%;
- }
- .col-sm-6 {
- width: 50%;
- }
- .col-sm-5 {
- width: 41.66666667%;
- }
- .col-sm-4 {
- width: 33.33333333%;
- }
- .col-sm-3 {
- width: 25%;
- }
- .col-sm-2 {
- width: 16.66666667%;
- }
- .col-sm-1 {
- width: 8.33333333%;
- }
- .col-sm-pull-12 {
- right: 100%;
- }
- .col-sm-pull-11 {
- right: 91.66666667%;
- }
- .col-sm-pull-10 {
- right: 83.33333333%;
- }
- .col-sm-pull-9 {
- right: 75%;
- }
- .col-sm-pull-8 {
- right: 66.66666667%;
- }
- .col-sm-pull-7 {
- right: 58.33333333%;
- }
- .col-sm-pull-6 {
- right: 50%;
- }
- .col-sm-pull-5 {
- right: 41.66666667%;
- }
- .col-sm-pull-4 {
- right: 33.33333333%;
- }
- .col-sm-pull-3 {
- right: 25%;
- }
- .col-sm-pull-2 {
- right: 16.66666667%;
- }
- .col-sm-pull-1 {
- right: 8.33333333%;
- }
- .col-sm-pull-0 {
- right: auto;
- }
- .col-sm-push-12 {
- left: 100%;
- }
- .col-sm-push-11 {
- left: 91.66666667%;
- }
- .col-sm-push-10 {
- left: 83.33333333%;
- }
- .col-sm-push-9 {
- left: 75%;
- }
- .col-sm-push-8 {
- left: 66.66666667%;
- }
- .col-sm-push-7 {
- left: 58.33333333%;
- }
- .col-sm-push-6 {
- left: 50%;
- }
- .col-sm-push-5 {
- left: 41.66666667%;
- }
- .col-sm-push-4 {
- left: 33.33333333%;
- }
- .col-sm-push-3 {
- left: 25%;
- }
- .col-sm-push-2 {
- left: 16.66666667%;
- }
- .col-sm-push-1 {
- left: 8.33333333%;
- }
- .col-sm-push-0 {
- left: auto;
- }
- .col-sm-offset-12 {
- margin-left: 100%;
- }
- .col-sm-offset-11 {
- margin-left: 91.66666667%;
- }
- .col-sm-offset-10 {
- margin-left: 83.33333333%;
- }
- .col-sm-offset-9 {
- margin-left: 75%;
- }
- .col-sm-offset-8 {
- margin-left: 66.66666667%;
- }
- .col-sm-offset-7 {
- margin-left: 58.33333333%;
- }
- .col-sm-offset-6 {
- margin-left: 50%;
- }
- .col-sm-offset-5 {
- margin-left: 41.66666667%;
- }
- .col-sm-offset-4 {
- margin-left: 33.33333333%;
- }
- .col-sm-offset-3 {
- margin-left: 25%;
- }
- .col-sm-offset-2 {
- margin-left: 16.66666667%;
- }
- .col-sm-offset-1 {
- margin-left: 8.33333333%;
- }
- .col-sm-offset-0 {
- margin-left: 0%;
- }
-}
-@media (min-width: 992px) {
- .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
- float: left;
- }
- .col-md-12 {
- width: 100%;
- }
- .col-md-11 {
- width: 91.66666667%;
- }
- .col-md-10 {
- width: 83.33333333%;
- }
- .col-md-9 {
- width: 75%;
- }
- .col-md-8 {
- width: 66.66666667%;
- }
- .col-md-7 {
- width: 58.33333333%;
- }
- .col-md-6 {
- width: 50%;
- }
- .col-md-5 {
- width: 41.66666667%;
- }
- .col-md-4 {
- width: 33.33333333%;
- }
- .col-md-3 {
- width: 25%;
- }
- .col-md-2 {
- width: 16.66666667%;
- }
- .col-md-1 {
- width: 8.33333333%;
- }
- .col-md-pull-12 {
- right: 100%;
- }
- .col-md-pull-11 {
- right: 91.66666667%;
- }
- .col-md-pull-10 {
- right: 83.33333333%;
- }
- .col-md-pull-9 {
- right: 75%;
- }
- .col-md-pull-8 {
- right: 66.66666667%;
- }
- .col-md-pull-7 {
- right: 58.33333333%;
- }
- .col-md-pull-6 {
- right: 50%;
- }
- .col-md-pull-5 {
- right: 41.66666667%;
- }
- .col-md-pull-4 {
- right: 33.33333333%;
- }
- .col-md-pull-3 {
- right: 25%;
- }
- .col-md-pull-2 {
- right: 16.66666667%;
- }
- .col-md-pull-1 {
- right: 8.33333333%;
- }
- .col-md-pull-0 {
- right: auto;
- }
- .col-md-push-12 {
- left: 100%;
- }
- .col-md-push-11 {
- left: 91.66666667%;
- }
- .col-md-push-10 {
- left: 83.33333333%;
- }
- .col-md-push-9 {
- left: 75%;
- }
- .col-md-push-8 {
- left: 66.66666667%;
- }
- .col-md-push-7 {
- left: 58.33333333%;
- }
- .col-md-push-6 {
- left: 50%;
- }
- .col-md-push-5 {
- left: 41.66666667%;
- }
- .col-md-push-4 {
- left: 33.33333333%;
- }
- .col-md-push-3 {
- left: 25%;
- }
- .col-md-push-2 {
- left: 16.66666667%;
- }
- .col-md-push-1 {
- left: 8.33333333%;
- }
- .col-md-push-0 {
- left: auto;
- }
- .col-md-offset-12 {
- margin-left: 100%;
- }
- .col-md-offset-11 {
- margin-left: 91.66666667%;
- }
- .col-md-offset-10 {
- margin-left: 83.33333333%;
- }
- .col-md-offset-9 {
- margin-left: 75%;
- }
- .col-md-offset-8 {
- margin-left: 66.66666667%;
- }
- .col-md-offset-7 {
- margin-left: 58.33333333%;
- }
- .col-md-offset-6 {
- margin-left: 50%;
- }
- .col-md-offset-5 {
- margin-left: 41.66666667%;
- }
- .col-md-offset-4 {
- margin-left: 33.33333333%;
- }
- .col-md-offset-3 {
- margin-left: 25%;
- }
- .col-md-offset-2 {
- margin-left: 16.66666667%;
- }
- .col-md-offset-1 {
- margin-left: 8.33333333%;
- }
- .col-md-offset-0 {
- margin-left: 0%;
- }
-}
-@media (min-width: 1200px) {
- .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
- float: left;
- }
- .col-lg-12 {
- width: 100%;
- }
- .col-lg-11 {
- width: 91.66666667%;
- }
- .col-lg-10 {
- width: 83.33333333%;
- }
- .col-lg-9 {
- width: 75%;
- }
- .col-lg-8 {
- width: 66.66666667%;
- }
- .col-lg-7 {
- width: 58.33333333%;
- }
- .col-lg-6 {
- width: 50%;
- }
- .col-lg-5 {
- width: 41.66666667%;
- }
- .col-lg-4 {
- width: 33.33333333%;
- }
- .col-lg-3 {
- width: 25%;
- }
- .col-lg-2 {
- width: 16.66666667%;
- }
- .col-lg-1 {
- width: 8.33333333%;
- }
- .col-lg-pull-12 {
- right: 100%;
- }
- .col-lg-pull-11 {
- right: 91.66666667%;
- }
- .col-lg-pull-10 {
- right: 83.33333333%;
- }
- .col-lg-pull-9 {
- right: 75%;
- }
- .col-lg-pull-8 {
- right: 66.66666667%;
- }
- .col-lg-pull-7 {
- right: 58.33333333%;
- }
- .col-lg-pull-6 {
- right: 50%;
- }
- .col-lg-pull-5 {
- right: 41.66666667%;
- }
- .col-lg-pull-4 {
- right: 33.33333333%;
- }
- .col-lg-pull-3 {
- right: 25%;
- }
- .col-lg-pull-2 {
- right: 16.66666667%;
- }
- .col-lg-pull-1 {
- right: 8.33333333%;
- }
- .col-lg-pull-0 {
- right: auto;
- }
- .col-lg-push-12 {
- left: 100%;
- }
- .col-lg-push-11 {
- left: 91.66666667%;
- }
- .col-lg-push-10 {
- left: 83.33333333%;
- }
- .col-lg-push-9 {
- left: 75%;
- }
- .col-lg-push-8 {
- left: 66.66666667%;
- }
- .col-lg-push-7 {
- left: 58.33333333%;
- }
- .col-lg-push-6 {
- left: 50%;
- }
- .col-lg-push-5 {
- left: 41.66666667%;
- }
- .col-lg-push-4 {
- left: 33.33333333%;
- }
- .col-lg-push-3 {
- left: 25%;
- }
- .col-lg-push-2 {
- left: 16.66666667%;
- }
- .col-lg-push-1 {
- left: 8.33333333%;
- }
- .col-lg-push-0 {
- left: auto;
- }
- .col-lg-offset-12 {
- margin-left: 100%;
- }
- .col-lg-offset-11 {
- margin-left: 91.66666667%;
- }
- .col-lg-offset-10 {
- margin-left: 83.33333333%;
- }
- .col-lg-offset-9 {
- margin-left: 75%;
- }
- .col-lg-offset-8 {
- margin-left: 66.66666667%;
- }
- .col-lg-offset-7 {
- margin-left: 58.33333333%;
- }
- .col-lg-offset-6 {
- margin-left: 50%;
- }
- .col-lg-offset-5 {
- margin-left: 41.66666667%;
- }
- .col-lg-offset-4 {
- margin-left: 33.33333333%;
- }
- .col-lg-offset-3 {
- margin-left: 25%;
- }
- .col-lg-offset-2 {
- margin-left: 16.66666667%;
- }
- .col-lg-offset-1 {
- margin-left: 8.33333333%;
- }
- .col-lg-offset-0 {
- margin-left: 0%;
- }
-}
.clearfix:before,
.clearfix:after,
.container:before,
.container:after,
.container-fluid:before,
.container-fluid:after,
-.row:before,
-.row:after {
- content: " ";
- display: table;
-}
.clearfix:after,
.container:after,
-.container-fluid:after,
-.row:after {
+.container-fluid:after {
clear: both;
}
.center-block {
diff --git a/plugins/Morpheus/stylesheets/base/font.css b/plugins/Morpheus/stylesheets/base/font.css
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/plugins/Morpheus/stylesheets/base/font.css
diff --git a/plugins/Morpheus/stylesheets/base/icons.css b/plugins/Morpheus/stylesheets/base/icons.css
index d11c5592e3..2ef29140d0 100644
--- a/plugins/Morpheus/stylesheets/base/icons.css
+++ b/plugins/Morpheus/stylesheets/base/icons.css
@@ -1,10 +1,10 @@
@font-face {
font-family: 'piwik';
- src: url('../../fonts/piwik.eot');
+ src: url('plugins/Morpheus/fonts/piwik.eot');
}
@font-face {
font-family: 'piwik';
- src: url('../../fonts/piwik.ttf');
+ src: url('plugins/Morpheus/fonts/piwik.ttf');
font-weight: normal;
font-style: normal;
}
diff --git a/plugins/Morpheus/stylesheets/base/mixins.less b/plugins/Morpheus/stylesheets/base/mixins.less
index 2558a14469..3795b90ce3 100644
--- a/plugins/Morpheus/stylesheets/base/mixins.less
+++ b/plugins/Morpheus/stylesheets/base/mixins.less
@@ -11,8 +11,11 @@
}
}
+strong {
+ font-weight: 700 !important;
+}
+
.font-default(@size: 13px, @line: 16px) {
- font-family: @theme-fontFamily-base;
font-size: @size;
line-height: @line;
}
diff --git a/plugins/Morpheus/stylesheets/general/_admin.less b/plugins/Morpheus/stylesheets/general/_admin.less
index c0f34c5e2d..6f9f1c26d6 100644
--- a/plugins/Morpheus/stylesheets/general/_admin.less
+++ b/plugins/Morpheus/stylesheets/general/_admin.less
@@ -8,34 +8,10 @@
}
}
-.adminTable {
- td {
- padding: 2px 0;
- }
- label {
- cursor: pointer;
- min-height: 30px;
- }
-}
-
.sites_autocomplete {
vertical-align: middle;
}
-.adminTable a {
- color: @theme-color-link;
-}
-
-.admin .adminTable a {
- color: @theme-color-text;
- text-decoration: underline;
-}
-
-.admin .adminTable .ui-inline-help a {
- color: @theme-color-link;
- text-decoration: none;
-}
-
.addRowSite,
.addrow {
cursor: pointer;
@@ -61,8 +37,3 @@
text-decoration: underline !important;
}
}
-
-#geoipdb-screen1>div>p {
- line-height: 1.4em;
- height: 6em;
-} \ No newline at end of file
diff --git a/plugins/Morpheus/stylesheets/general/_form.less b/plugins/Morpheus/stylesheets/general/_form.less
index ebd56acbb3..9a21d76ed4 100644
--- a/plugins/Morpheus/stylesheets/general/_form.less
+++ b/plugins/Morpheus/stylesheets/general/_form.less
@@ -31,23 +31,10 @@
margin: 0 1px 0 0 !important;
}
-.entityAddContainer .entityTable th, #entityEditContainer .entityTable th, #entityEditContainer .entityTable td {
- vertical-align: middle !important;
-}
-
-.entityAddContainer .entityTable tr td.first {
- font-weight: bold;
- vertical-align: top !important;
-}
-
-.entityAddContainer tr:hover {
- background: none !important;
-}
-
/* Add / Edit / List entities */
.entityContainer {
width: 100%;
- max-width: 850px;
+ max-width: 900px;
min-width: 600px;
font-size: 14px;
}
@@ -58,79 +45,8 @@
clear: both;
}
-table.entityTable {
- thead {
- tr {
- th {
- background-color: @color-silver-l98 !important;
- text-align: left !important;
- font-size: 16px;
- }
-
- td {
- padding: 10px;
- }
- }
- }
-
- tr {
- td {
- padding:10px;
- }
- }
-}
-
-.entityTable tr td.first, .entityTable tr th.first {
- border-left: 0 !important;
- vertical-align: top !important;
-}
-
-.entityTable tr th {
- white-space: nowrap;
- padding: 16px 10px !important;
-}
-
-.entityTable tr td, .entityTable tr th {
- border-bottom: 0 !important;
-}
-
-table.entityTable tr td a {
- text-decoration: underline;
-}
-
-.entityTable tr.highlighted td {
- background-color: #ECF9DD;
-}
-
/* cancel button below Forms */
.entityCancel {
padding: 10px 0;
font-size: 12px;
}
-
-/* List with grey arrows on left */
-.entityList ul.listCircle {
- font-weight: normal;
- list-style: none;
- padding: 4px 0;
-}
-
-.entityList ul li {
- background: @theme-color-background-base url(plugins/Morpheus/images/li_dbl_gray.gif) no-repeat 6px 10px;
- padding: 0 0 0 21px;
- line-height: 22px;
-}
-
-.entityList ul li .dimension {
- cursor: pointer;
- border-bottom: 1px solid #d0d0d0;
-}
-
-.entityList ul li.activeDimension .dimension {
- font-weight: bold;
- border: 0;
-}
-
-.entityList ul.listCircle li a {
- color: #000;
-}
diff --git a/plugins/Morpheus/stylesheets/general/_forms.less b/plugins/Morpheus/stylesheets/general/_forms.less
index dcbe261807..cc56ae93b0 100644
--- a/plugins/Morpheus/stylesheets/general/_forms.less
+++ b/plugins/Morpheus/stylesheets/general/_forms.less
@@ -1,491 +1,67 @@
-form {
- margin-bottom: 30px;
-}
-
-label {
- cursor: pointer;
-}
-
-input:not([type="checkbox"]):not([type="submit"]):not([type="button"]), select, textarea {
- border: 1px solid #d4d4d4;
- font-size: 14px;
- color: @theme-color-text-lighter;
- .border-radius(2px);
- margin-left: 0;
- padding: 10px 12px;
- min-height: 20px;
- .box-sizing(border-box);
- background: @theme-color-background-base;
-}
-input:not([type="checkbox"]):not([type="submit"]):not([type="button"]), textarea {
- -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
- box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-}
-input[disabled] {
- background: #F5F5F5 none !important;
-}
-
-select {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- background-color: @theme-color-background-base;
- background-image: url("plugins/Morpheus/images/select_arrow.png");
- background-repeat: no-repeat;
- background-position: right center;
- padding-right: 30px;
-}
-// This class/mixin allows to restore the native style of selects when needed
-.native-select {
- -webkit-appearance: menulist;
- -moz-appearance: menulist;
- appearance: menulist;
- padding: 4px 4px;
- background: none;
-}
-
.form-group {
- width: 100%;
- margin-bottom: 15px;
- .clearfix();
-
- label {
- display: block;
- margin: 8px 0 6px;
- font-size: 14px;
- line-height: 20px;
- width: 40%;
- }
- input, select, textarea {
- display: block;
- width: 40%;
- }
- .input-group {
- width: 40%;
- }
-
- .radio, .checkbox {
- font-size: 12px;
- width: 40%;
- text-transform: none;
- line-height: 20px;
- margin: 7px 0 2px;
- input[type="checkbox"], input[type="radio"] {
- display: inline;
- width: auto;
- }
- // Quickform adds a label automatically so we have to remove its formatting
- // TODO remove when we get rid of Quickform
- label {
- text-transform: none;
- display: inline;
- width: auto;
- font-size: inherit;
- }
- .form-description {
- display: inline;
- }
- }
-
- .form-help {
- float: right;
- width: 58%;
- margin-bottom: 0;
- }
-
- label .form-description {
- display: inline;
- &:before {
- content: '(';
- }
- &:after {
- content: ')';
- }
- }
-
- .form-description {
- width: 40%;
- display: block;
- margin-top: 6px;
- }
-
- .sites_autocomplete {
- position: relative;
- }
-}
-
-// TODO Form helps are used outside of forms in UserCountry (old table layout)
-// Once this plugin uses the new form layout, move those rules back into ".form-group .form-help"
-.form-help {
- .alert;
- .alert-info;
- // Smaller padding to match input's heights
- padding: 11px 20px 10px;
- &:before {
- // Remove the icon
- content: "";
- }
-}
-
-.form-description {
- color: @theme-color-text-lighter;
- font-style: italic;
-}
-
-.add-cors-host,
-.submit {
- .btn;
-}
-
-.top_bar_sites_selector {
- z-index: 143;
- .sites_autocomplete .custom_select {
- z-index: 139;
- }
-}
-
-.sites_autocomplete {
- input {
- min-height: 0;
- }
-}
-
-.ajaxError {
- background: @theme-color-brand;
- color: #fff;
- border: 0px;
- .border-radius(6px);
- padding: 20px 25px;
- text-align: center;
- font-weight: normal;
-}
-
-.limitSelection {
- > ul {
- position: relative;
- top: -6px;
- }
- > div {
- .border-radius(2px);
- background-color: @theme-color-background-base;
- .font-default(10px, 12px);
- background: none;
- padding: 2px 14px 2px 1px;
- span {
- position: relative;
- background: none;
- color: @theme-color-text;
- display: block;
- padding-right: 3px;
- &:after {
- content: '';
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
- border-top: 5px solid @theme-color-brand;
- position: absolute;
- top: 7px;
- right: -9px;
- }
- }
- }
-
- span {
- color: @theme-color-text;
- font-weight: normal;
- }
+ margin-bottom: 20px;
+ margin-top: 20px;
- &.visible {
- > div {
- background-image: none;
- > span:after {
- content: '';
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
- border-bottom: 5px solid @theme-color-brand;
- border-top-color: transparent;
- position: absolute;
- top: 1px;
- right: -9px;
- }
- }
- }
-}
-
-//checkboxes and radio
-.form-radio, .form-checkbox {
- display: inline-block;
- padding: 0;
- margin: 0;
-}
-.form-radio, .form-checkbox {
- height: 15px;
- width: 15px;
- float: left;
- cursor: pointer;
- background: url('plugins/Morpheus/images/forms-sprite.png');
- margin: 2px 5px 2px 0;
-}
-
-.form-radio.disabled {
- background-position: 15px -16px;
-}
-
-.form-checkbox.disabled {
- background-position: 15px 0px;
-}
-
-.form-radio + label, .form-checkbox + label {
- float: left;
- display: inline-block;
-
- + .form-description {
- margin-left: 1em;
- }
-
- + br, + .form-description + br {
- clear: both;
- }
-
- + .form-radio, + .form-checkbox {
- margin-left: 32px;
- }
-}
+ label.siteSelectorLabel {
+ position: relative;
+ top: -7px;
+ left: 0;
+ font-size: 13px;
+ }
-fieldset > .form-radio + label { // assumes <br/> after the label
+ label.fieldRadioTitle {
+ padding-bottom: 10px;
display: inline-block;
- margin-bottom: -.5em;
-}
-
-label {
- &.hover,
- &:hover {
- border: 0px;
- }
-}
+ color: #9e9e9e;
+ font-size: 13px;
-.form-radio {
- background-position: 0 -16px;
+ }
- &.form-hover {
- background-position: -60px -16px;
- }
-
- &.checked {
- background-position: -30px -16px;
- &.form-hover {
- background-position: -90px -16px;
- }
- }
-}
+ > h3 {
+ padding-bottom: 16px !important;
+ }
-.form-checkbox {
- background-position: 0 0;
-
- &.form-hover {
- background-position: -60px 0;
- }
-
- &.checked {
- background-position: -30px 0;
- &.form-hover {
- background-position: -90px 0;
- }
- }
-}
+ .form-help {
+ color: #838383;
+ background-color: #F5F5F5;
+ font-size: 13px;
+ padding: 20px 20px 20px 20px;
+ border: 1px solid rgba(0, 0, 0, 0);
+ border-radius: 2px;
+ font-size: 14px;
+ position: relative;
+ }
-.prettycheckbox a:focus, .prettyradio a:focus {
- outline: 0 none;
-}
-.prettycheckbox label, .prettyradio label {
+ label {
+ .form-description {
+ font-size: 90%;
+ font-style: italic;
+ &:before {
+ content: '(';
+ }
+ &:after {
+ content: ')';
+ }
+ }
+ }
+
+ .inline-help .inline-help-node {
display: block;
- float: left;
- margin: 2px;
- cursor: pointer;
-}
-.prettycheckbox a.disabled, .prettycheckbox label.disabled, .prettyradio a.disabled, .prettyradio label.disabled {
- cursor: not-allowed;
-}
-.prettycheckbox a {
- background-position: 0 0;
-}
-.prettycheckbox a:focus {
- background-position: -15px 0;
-}
-.prettycheckbox a.checked {
- background-position: -38px 0;
-}
-.prettycheckbox a.checked:focus {
- background-position: -53px 0;
-}
-.prettycheckbox a.checked.disabled {
- background-position: -99px 0;
-}
-.prettycheckbox a.disabled {
- background-position: -76px 0;
-}
-.prettyradio a {
- background-position: -129px 0;
-}
-.prettyradio a:focus {
- background-position: -144px 0;
-}
-.prettyradio a.checked {
- background-position: -167px 0;
-}
-.prettyradio a.checked:focus {
- background-position: -182px 0;
-}
-.prettyradio a.checked.disabled {
- background-position: -228px 0;
-}
-.prettyradio a.disabled {
- background-position: -205px 0;
-}
+ }
-// specific form control overrides (for iCheck)
-.indented-radio-button {
- margin: 0;
}
-.listReports > li:after {
- content: "";
- display: table;
- clear: both;
-}
-.listReports {
- .form-radio + label, .form-checkbox + label {
- width: 250px;
- }
+.inline-help-node {
+ display: none;
}
-// Bootstrap input groups
-// --------------------------------------------------
-.input-group {
- position: relative; // For dropdowns
- display: table;
- border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table
-
- // Undo padding and float of grid classes
- &[class*="col-"] {
- float: none;
- padding-left: 0;
- padding-right: 0;
- }
-
- input {
- // Ensure that the input is always above the *appended* addon button for
- // proper border colors.
- position: relative;
- z-index: 2;
-
- // IE9 fubars the placeholder attribute in text inputs and the arrows on
- // select elements in input groups. To fix it, we float the input. Details:
- // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855
- float: left;
-
- width: 100%;
- margin-bottom: 0;
- }
+.input-field .prefix {
+ color: @theme-color-text-lighter;
}
-// Display as table-cell
-.input-group-addon,
-.input-group-btn,
-.input-group input {
- display: table-cell;
- &:not(:first-child):not(:last-child) {
- border-radius: 0;
- }
-}
-// Addon and addon wrapper for buttons
-.input-group-addon,
-.input-group-btn {
- width: 1%;
- white-space: nowrap;
- vertical-align: middle; // Match the inputs
-}
-// Text input groups
-.input-group-addon {
- padding: 6px 12px;
- font-size: 14px;
- font-weight: normal;
- color: @theme-color-text-lighter;
- line-height: 1;
- text-align: center;
- background-color: @theme-color-background-base;
- border: 1px solid #d4d4d4;
- border-radius: 2px;
- // Nuke default margins from checkboxes and radios to vertically center within.
- input[type="radio"],
- input[type="checkbox"] {
- margin-top: 0;
- }
-}
-
-// Reset rounded corners
-.input-group input:first-child,
-.input-group-addon:first-child,
-.input-group-btn:first-child > .btn,
-.input-group-btn:first-child > .btn-group > .btn,
-.input-group-btn:first-child > .dropdown-toggle,
-.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
-.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.input-group-addon:first-child {
- border-right: 0;
-}
-.input-group input:last-child,
-.input-group-addon:last-child,
-.input-group-btn:last-child > .btn,
-.input-group-btn:last-child > .btn-group > .btn,
-.input-group-btn:last-child > .dropdown-toggle,
-.input-group-btn:first-child > .btn:not(:first-child),
-.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.input-group-addon:last-child {
- border-left: 0;
-}
-// Button input groups
-.input-group-btn {
- position: relative;
- // Jankily prevent input button groups from wrapping with `white-space` and
- // `font-size` in combination with `inline-block` on buttons.
- font-size: 0;
- white-space: nowrap;
-
- // Negative margin for spacing, position for bringing hovered/focused/actived
- // element above the siblings.
- > .btn {
- position: relative;
- + .btn {
- margin-left: -1px;
- }
- // Bring the "active" button to the front
- &:hover,
- &:focus,
- &:active {
- z-index: 2;
- }
- }
-
- // Negative margin to only have a 1px border between the two
- &:first-child {
- > .btn,
- > .btn-group {
- margin-right: -1px;
- }
- }
- &:last-child {
- > .btn,
- > .btn-group {
- z-index: 2;
- margin-left: -1px;
- }
- }
-}
+input.browser-default {
+ box-sizing: border-box;
+ margin: 0;
+ height: auto;
+} \ No newline at end of file
diff --git a/plugins/Morpheus/stylesheets/general/_jqueryUI.less b/plugins/Morpheus/stylesheets/general/_jqueryUI.less
index 51d5cd0b9a..77dbe5df64 100644
--- a/plugins/Morpheus/stylesheets/general/_jqueryUI.less
+++ b/plugins/Morpheus/stylesheets/general/_jqueryUI.less
@@ -40,6 +40,10 @@
border-radius: 3px;
}
+.ui-widget {
+ font-family: @theme-fontFamily-base;
+}
+
/* extending the jquery UI css, for visitor Log popover */
.ui-widget .dataTable {
font-size: 14px;
@@ -157,8 +161,18 @@ div.ui-state-highlight {
text-align: center !important;
}
+.ui-datepicker .ui-state-default {
+ background: @theme-color-background-contrast !important;
+}
+
+.ui-datepicker td.ui-datepicker-current-day .ui-state-default {
+ background: @theme-color-text !important;
+ color: @theme-color-background-contrast !important;
+}
+
.ui-datepicker td.ui-datepicker-week-end .ui-state-default {
background: #f6f6f6 !important;
+ color: @theme-color-text !important;
}
.ui-datepicker td.ui-datepicker-current-period a.ui-state-default, td .ui-state-active, .ui-datepicker td.ui-datepicker-current-period a.ui-state-active, .ui-datepicker td.ui-datepicker-week-end .ui-state-active, .ui-datepicker td.ui-datepicker-other-month.ui-datepicker-current-period {
@@ -273,9 +287,11 @@ body .ui-tooltip.small {
font-size:8px;
background-image: none !important;
- text-indent: 0px !important;
+ text-indent: 0 !important;
opacity: 1 !important;
color:@theme-color-brand-contrast;
+ overflow: visible;
+ margin-top: -4px;
&:before{
content:"\e60a";
diff --git a/plugins/Morpheus/stylesheets/general/_misc.less b/plugins/Morpheus/stylesheets/general/_misc.less
index e26c52f798..d3086d5bce 100644
--- a/plugins/Morpheus/stylesheets/general/_misc.less
+++ b/plugins/Morpheus/stylesheets/general/_misc.less
@@ -28,10 +28,10 @@
.reportsByDimensionView > .entityList {
float: left;
- width: 220px;
- min-height: 450px;
+ width: 208px;
+ min-height: 200px;
}
.dimensionCategory {
- margin-top: 10px;
+ margin-top: 16px;
} \ No newline at end of file
diff --git a/plugins/Morpheus/stylesheets/general/_typography.less b/plugins/Morpheus/stylesheets/general/_typography.less
index 7c15478723..66b2dddeb4 100644
--- a/plugins/Morpheus/stylesheets/general/_typography.less
+++ b/plugins/Morpheus/stylesheets/general/_typography.less
@@ -11,8 +11,9 @@ h1 {
font-size: 30px;
margin-bottom: 20px;
}
+
h2 {
- color: @theme-color-text;
+ color: @theme-color-headline-alternative;
}
.datatableRelatedReports {
@@ -20,21 +21,8 @@ h2 {
}
.tableIcon {
- background: none;
- padding: 2px 4px 4px 4px;
&.activeIcon {
- background: @color-silver-l80;
- }
-}
-
-.exportToFormatItems {
- background: @color-silver-l80;
- padding-top: 5px;
- padding-bottom: 4px;
- color: #000;
-
- a {
- color: @theme-color-brand !important;
+ background-color: @theme-color-background-tinyContrast;
}
}
@@ -53,7 +41,7 @@ h2 {
.dataTablePages {
color: @theme-color-text;
font-weight: normal;
- .font-default(10px, 12px);
+ .font-default(13px, 14px);
}
.datatableRelatedReports {
@@ -63,11 +51,6 @@ h2 {
font-weight: normal;
}
}
-.dataTableSearchPattern {
- img {
- top: 7px !important
- }
-}
.tagCloud span, .tagCloud span a {
color: @theme-color-link !important;
@@ -103,14 +86,3 @@ body > a.ddmetric {
color: #000 !important;
font-family: @theme-fontFamily-base !important;
}
-
-// Bootstrap classes (can be removed in the future)
-.text-left {
- text-align: left;
-}
-.text-center {
- text-align: center;
-}
-.text-right {
- text-align: right;
-}
diff --git a/plugins/Morpheus/stylesheets/ieonly.css b/plugins/Morpheus/stylesheets/ieonly.css
deleted file mode 100644
index 45523c856a..0000000000
--- a/plugins/Morpheus/stylesheets/ieonly.css
+++ /dev/null
@@ -1,37 +0,0 @@
-.ui-dialog.ui-widget-content {
- z-index: 1003!important;
-}
-
-.ui-widget-overlay {
- z-index: 1002!important;
-}
-
-.widgetContent {
- width: 99.8%;
- overflow: hidden;
-}
-
-#topLeftBar {
- z-index: 300!important;
-}
-
-.submit {
- height: 30px;
-}
-
-input[type=checkbox], input[type=radio] {
- border: none;
- background: transparent none;
-}
-
-.jqplot-seriespicker-popover p input {
- width: 15px;
- margin: 0;
- padding: 0;
-}
-
-/* Don't override select style */
-select {
- background: transparent;
- padding-right: 12px;
-}
diff --git a/plugins/Morpheus/stylesheets/main.less b/plugins/Morpheus/stylesheets/main.less
index 94e08389f9..8f2529d93f 100644
--- a/plugins/Morpheus/stylesheets/main.less
+++ b/plugins/Morpheus/stylesheets/main.less
@@ -13,34 +13,62 @@
body {
background: @theme-color-background-base;
font-family: @theme-fontFamily-base;
+ color: @theme-color-text;
+}
+
+button, input, optgroup, select, textarea {
+ color: @theme-color-text;
}
h2 {
font-weight: normal;
- border-bottom: 1px solid @color-gray;
- margin: 15px -15px 20px 0;
font-size: 24px;
width: 100%;
+ padding: 16px 0 16px 0;
+ margin: 0;
a, a:hover {
text-decoration: none;
- color: @color-black-piwik;
+ color: @theme-color-text;
}
}
h3 {
- color: @theme-color-text;
+ color: @theme-color-headline-alternative;
.font-default(18px, 24px);
font-weight: normal;
+ margin: 32px 0 16px 0;
}
-a:hover {
+
+.pageWrap a:hover {
text-decoration:underline;
}
-#content {
- p {
- margin-left: 0px;
- margin-right: 0px;
- .font-default(13px, 18px);
- }
+p {
+ color: @theme-color-text;
+}
+
+#content p {
+ margin-left: 0;
+ margin-right: 0;
+ .font-default(13px, 18px);
+}
+
+.piwik-content-intro {
+ button, input, optgroup, select, textarea {
+ color: @theme-color-headline-alternative;
+ }
+ color: @theme-color-headline-alternative;
+ h1, h2, h3, h4 {
+ color: @theme-color-headline-alternative;
+ }
+ p {
+ color: @theme-color-headline-alternative;
+ }
+}
+
+#content .card-content {
+ p {
+ color: @theme-color-text;
+ }
}
#leftcolumn {
@@ -53,22 +81,6 @@ a:hover {
width: 50%;
}
-.entityTable {
- .border-radius(0px)!important;
- border: 1px solid @color-silver-l90 !important;
- tr {
- &.first {
- th {
- border-left: 0px !important;
- }
- }
- }
-}
-
-table.entityTable tr td a:hover {
- text-decoration: underline !important;
-}
-
#root {
margin: 0 0 100px 0;
padding: 0;
@@ -133,13 +145,13 @@ table.entityTable tr td a:hover {
background: @theme-color-background-base;
.border-radius(0px);
.dropdown-body {
- background:@theme-color-background-base;
+ background:@theme-color-background-contrast;
padding: 8px 19px 0;
.border-radius(0px);
border-top-width: 0px;
}
&:hover .dropdown-body {
- background:@theme-color-background-base;
+ background:@theme-color-background-contrast;
}
.segmentationContainer {
> span > strong {
@@ -199,7 +211,7 @@ table.entityTable tr td a:hover {
white-space: nowrap;
margin-right: 0;
a {
- font-family: Verdana, sans-serif;
+ font-family: @theme-fontFamily-base;
font-size: 11px;
line-height: 12px;
display: inline-block;
@@ -239,7 +251,7 @@ table.entityTable tr td a:hover {
.rss-date {
display: block;
color: @color-silver-l60;
- .font-default(11px, 26px);
+ .font-default(13px, 26px);
}
.rss-description {
@@ -254,12 +266,12 @@ table.dataTable {
thead {
tr {
th {
- background: @color-silver-l98;
+ background: @theme-color-background-contrast;
text-transform: uppercase;
color: @theme-color-text;
.font-default(11px, 12px);
- padding-top: 12px;
- padding-bottom: 12px;
+ padding-top: 16px;
+ padding-bottom: 16px;
vertical-align: middle;
&:not(.first) {
@@ -274,7 +286,8 @@ table.dataTable {
}
#thDIV {
- display: inline;
+ display: inline;
+ position: relative;
}
&.columnSorted {
@@ -327,7 +340,7 @@ table.dataTable {
border-bottom: 1px solid @color-silver-l95 !important;
border-color: @color-silver-l95 !important;
color: @theme-color-text;
- background: @theme-color-background-base;
+ background: @theme-color-background-contrast;
&:not(.value) {
.font-default(13px, 16px);
@@ -378,6 +391,7 @@ table.dataTable {
text-overflow: ellipsis;
width: inherit;
display: inline-block;
+ vertical-align: sub;
}
}
@@ -387,24 +401,14 @@ table.dataTable {
}
.cellSubDataTable td {
- background: @theme-color-background-base;
+ background: @theme-color-background-contrast;
}
.cellSubDataTable tr:hover td {
- background-color: @color-silver-l95;
+ background-color: @theme-color-background-base;
}
}
}
- &.entityTable tr {
- td {
- background-color: @theme-color-background-base !important;
- }
- &.inactive-plugin td,
- &:hover td {
- background-color: @color-silver-l95 !important;
- }
- }
-
&.dataTableVizVisitorLog {
}
@@ -418,7 +422,7 @@ div.dataTableVizHtmlTable:not(.dataTableActions), div.dataTableVizAllColumns, di
width: 12px;
height: 12px;
margin-left:-1px;
- margin-top:4px;
+ margin-top:3px;
margin-right:8px;
content: '';
}
@@ -432,10 +436,6 @@ div.dataTableVizHtmlTable:not(.dataTableActions), div.dataTableVizAllColumns, di
}
}
-table.dataTable .dataTableRowActions {
- margin-top: -7px;
-}
-
.visitsLiveFooter {
padding-left: 10px;
a.rightLink {
@@ -446,13 +446,8 @@ table.dataTable .dataTableRowActions {
}
}
-.dataTableFooterIcons {
- padding: 6px 12px 0px;
- border-top: 1px solid @color-silver-l90;
-}
-
.UserCountryMap-btn-zoom {
- padding-left: 0px;
+ padding-left: 0;
}
div.sparkline {
@@ -487,57 +482,13 @@ div.sparkline {
.dataTableNext, .dataTablePrevious {
color: @theme-color-link;
- text-transform: uppercase;
- .font-default(10px, 12px);
+ .font-default(13px, 14px);
}
.UserCountryMap-info-btn {
z-index: 1;
}
-.dataTableSearchPattern {
- background: none;
- height: auto;
- input {
- color: @theme-color-text !important;
- .border-radius(0px);
- margin-left: 0;
- padding: 8px 10px;
- min-height: 30px !important;
- .box-sizing(border-box);
- border: 1px solid @color-silver-l80;
- .opacity(1);
-
- &[type="text"] {
- width: 40% !important;
- padding-right: 30px !important;
- padding-bottom: 2px !important;
- &:focus {
- border-color: @color-silver-l60;
- }
- }
-
- &[type="submit"] {
- margin: 0;
- float: none;
- .font-default(10px, 12px) !important;
- padding: 0 !important;
- position: relative;
- right: 33px;
- background: no-repeat 0 7px url('plugins/Morpheus/images/search_ico.png') !important;
- text-indent: 999999px;
- width: 17px !important;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
- }
- }
-}
-
-.dataTable .searchReset>img {
- left: -45px;
-}
-
.visitor-profile a {
color: @theme-color-link;
font-weight: normal;
@@ -601,15 +552,15 @@ div.sparkline {
}
ol {
- background: @theme-color-background-base !important;
+ background: @theme-color-background-contrast !important;
border-top: 0 !important;
li {
- background: @theme-color-background-base !important;
+ background: @theme-color-background-contrast !important;
.font-default(11px, 19px);
font-weight: normal;
color: @theme-color-text-lighter;
&:last-child {
- border-bottom: 0px !important;
+ border-bottom: 0 !important;
}
}
}
@@ -670,7 +621,7 @@ div.sparkline {
strong {
.font-default(13px, 18px);
color: @theme-color-text;
- font-weight: normal;
+ font-weight: normal !important;
}
span {
.font-default(13px, 18px);
@@ -695,7 +646,7 @@ div.sparkline {
strong {
color: @theme-color-text;
.font-default(13px, 18px);
- font-weight: normal;
+ font-weight: normal !important;
}
.visitor-profile-os,
@@ -748,10 +699,6 @@ div.sparkline {
height: 6px;
}
-.dataTableFooterIcons, .dataTableFooterIcons a {
- color: @theme-color-link;
-}
-
#visitsLive .datetime {
background: @color-silver-l95;
border-top: 0;
@@ -767,10 +714,6 @@ div.sparkline {
.font-default(10px, 16px);
}
-#token_auth {
- background: @color-silver-l95;
-}
-
tr:hover #token_auth {
background: #FFFFF7;
}
@@ -786,6 +729,22 @@ tr:hover #token_auth {
#multisites table.dataTable {
tfoot tr:hover td {
- background: @theme-color-background-base;
+ background: @theme-color-background-contrast;
}
}
+
+/** Materialize color overwrites */
+.dropdown-content li>a, .dropdown-content li>span {
+ color: @theme-color-link !important;
+}
+
+.dropdown-content li:hover, .dropdown-content li.active, .dropdown-content li.selected {
+ background-color: @theme-color-background-tinyContrast;
+}
+
+#root .side-nav .collapsible-body li a {
+ padding-top: 20px;
+ line-height: 20px;
+ height: auto;
+ padding-bottom: 20px;
+} \ No newline at end of file
diff --git a/plugins/Morpheus/stylesheets/theme-advanced.less b/plugins/Morpheus/stylesheets/theme-advanced.less
index 31e91e6cd8..c6b90be4ba 100644
--- a/plugins/Morpheus/stylesheets/theme-advanced.less
+++ b/plugins/Morpheus/stylesheets/theme-advanced.less
@@ -1,7 +1,7 @@
-@theme-color-background-base: #fff;
+@theme-color-background-base: #edecec;
@theme-color-background-tinyContrast: #f2f2f2;
@theme-color-background-lowContrast: @color-silver-l85;
-@theme-color-background-contrast: #5F5A60;
+@theme-color-background-contrast: #ffffff;
@theme-color-background-highContrast: #202020;
@theme-color-border: @color-silver-l80;
@@ -9,5 +9,5 @@
@theme-color-code: #F3F3F3;
@theme-color-code-background: #4D4D4D;
-@theme-color-widget-background: @theme-color-background-base;
-@theme-color-widget-border: @color-silver-l85;
+@theme-color-widget-background: @theme-color-background-contrast;
+@theme-color-widget-border: @theme-color-background-tinyContrast;
diff --git a/plugins/Morpheus/stylesheets/theme.less b/plugins/Morpheus/stylesheets/theme.less
index 54a49bd9db..dacc68042f 100644
--- a/plugins/Morpheus/stylesheets/theme.less
+++ b/plugins/Morpheus/stylesheets/theme.less
@@ -1,6 +1,6 @@
// Other theme variables
// Variable pattern: @theme-type-<role>
-@theme-fontFamily-base: Verdana, sans-serif;
+@theme-fontFamily-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Cantarell, "Helvetica Neue", sans-serif;
// Theme colors
// Variable pattern: @theme-color-<role>[-<modifier>]
@@ -9,13 +9,20 @@
@theme-color-text: @color-black-piwik;
@theme-color-text-light: #444;
@theme-color-text-lighter: @color-silver-l40;
+@theme-color-text-contrast: #777;
@theme-color-link: @color-blue-piwik;
@theme-color-base-series: #ee3024;
+@theme-color-headline-alternative: #4E4E4E;
+
+@theme-color-header-background: #37474f;
+
@theme-color-menu-contrast-text: @theme-color-text;
@theme-color-menu-contrast-textSelected: @theme-color-menu-contrast-text;
@theme-color-menu-contrast-textActive: @theme-color-brand;
-@theme-color-menu-contrast-background: @theme-color-background-tinyContrast;
+@theme-color-menu-contrast-background: @theme-color-background-contrast;
+
+@theme-color-widget-exported-background-base: @theme-color-background-contrast;
@theme-color-widget-title-text: @theme-color-text;
-@theme-color-widget-title-background: @theme-color-background-tinyContrast;
+@theme-color-widget-title-background: @theme-color-background-contrast;
diff --git a/plugins/Morpheus/stylesheets/ui/_alerts.less b/plugins/Morpheus/stylesheets/ui/_alerts.less
deleted file mode 100644
index c3f1dee5e6..0000000000
--- a/plugins/Morpheus/stylesheets/ui/_alerts.less
+++ /dev/null
@@ -1,64 +0,0 @@
-.alert-icon-center-vertically(@font-size) {
- @half-height: @font-size / 2;
- // IE8 doesn't support calc(): it's OK, the icon will just be aligned to the top
- top: calc(~'50% - @{half-height}');
- // phantomjs only supports -webkit-calc()
- top: -webkit-calc(~'50% - @{half-height}');
-}
-
-.alert {
- padding: 20px 20px 20px 60px;
- margin-bottom: 20px;
- border: 1px solid transparent;
- border-radius: 2px;
- font-size: 14px;
- position: relative;
- &:before {
- font-family: "piwik";
- content: "\e625";
- position: absolute;
- left: 20px;
- line-height: 100%; // line-height = font-size
- font-size: 24px;
- }
-
- a {
- color: inherit;
- text-decoration: underline;
- }
-}
-.alert-success {
- color: #009874;
- border-color: #1AA282;
- &:before {
- content: "\e63d";
- color: #1AA282;
- }
-}
-.alert-info {
- color: #838383;
- background-color: #F5F5F5;
- font-size: 13px;
- padding-top: 15px;
- padding-bottom: 15px;
- &:before {
- color: #afafaf;
- font-size: 20px;
- }
-}
-.alert-warning {
- color: #CA8100;
- border-color: #DF9D27;
- &:before {
- content: "\e621";
- color: #DF9D27;
- }
-}
-.alert-danger {
- color: #D4291F;
- border-color: #D73F36;
- &:before {
- content: "\e616";
- color: #D73F36;
- }
-}
diff --git a/plugins/Morpheus/stylesheets/ui/_buttons.less b/plugins/Morpheus/stylesheets/ui/_buttons.less
index ce5417007d..6aaccdd9f4 100644
--- a/plugins/Morpheus/stylesheets/ui/_buttons.less
+++ b/plugins/Morpheus/stylesheets/ui/_buttons.less
@@ -1,18 +1,15 @@
// We use `button:not(.btn)` because `button` has a higher priority than CSS classes
// which makes it impossible to use btn-lg or similar additional classes.
-button:not(.btn),
-input[type="submit"]:not(.btn),
-.btn {
+
+button.btn,input[type="submit"].btn, .btn {
display: inline-block;
.border-radius(3px);
background: none;
- color: @theme-color-brand-contrast;
background-color: @theme-color-brand;
- box-shadow: 0 1px 1px 0 rgba(13, 13, 13, 0.3);
+ color: @theme-color-brand-contrast !important;
#gradient > .vertical(rgba(255,255,255,.15), rgba(255,255,255,0));
- .font-default(12px, 16px);
+ font-size: 12px;
font-weight: normal;
- padding: 5px 15px;
text-align: center;
text-decoration: none;
cursor: pointer;
@@ -29,16 +26,22 @@ input[type="submit"]:not(.btn),
}
}
+.btn-flat:hover {
+ background-color: @theme-color-background-base;
+ text-decoration: none !important;
+ box-shadow: 0 0;
+}
+
+.btn.btn-small {
+ padding: 0 16px;
+}
+
+
// Bootstrap classes (can be removed in the future)
.btn {
display: inline-block;
}
-.btn-lg {
- padding: 12px 40px;
- font-size: 14px;
-}
.btn-block {
- display: block;
width: 100%;
}
.btn-block + .btn-block {
@@ -51,12 +54,6 @@ input[type="submit"]:not(.btn),
opacity: .65;
}
// See http://getbootstrap.com/css/#buttons-options
-.btn.btn-link {
- background: transparent;
- color: @theme-color-link;
- text-decoration: underline;
- box-shadow: none;
-}
.btn.btn-noop {
background: transparent;
color: @theme-color-text;
@@ -64,18 +61,3 @@ input[type="submit"]:not(.btn),
cursor: not-allowed;
box-shadow: none;
}
-
-.btn.btn-flat {
- background: transparent;
- border-radius: 0;
- color: @theme-color-text-light;
- box-shadow: none;
- padding: 5px 10px;
- &:hover {
- background-color: #eaeaea;
- text-decoration: none;
- }
- &.btn-lg {
- padding: 12px;
- }
-}
diff --git a/plugins/Morpheus/stylesheets/ui/_cards.less b/plugins/Morpheus/stylesheets/ui/_cards.less
index 3a87e02f13..0daebd4601 100644
--- a/plugins/Morpheus/stylesheets/ui/_cards.less
+++ b/plugins/Morpheus/stylesheets/ui/_cards.less
@@ -1,15 +1,36 @@
+h1, h2, h3, h4 {
+ &.card-title {
+ color: @theme-color-text !important;
+ a {
+ color: @theme-color-text !important;
+ }
+ }
+}
+
.card {
- color: @theme-color-text-light;
- padding: 15px;
- margin: 9px 0;
- border-radius: 2px;
- background-color: #fff;
- -webkit-box-shadow: 0 1px 6px 0 rgba(0, 0, 0, 0.3);
- -moz-box-shadow: 0 1px 6px 0 rgba(0, 0, 0, 0.3);
- box-shadow: 0 1px 6px 0 rgba(0, 0, 0, 0.3);
- position: relative;
-
- &:hover {
- background-color: @color-silver-l95;
+ h1, h2, h3, h4 {
+ color: @theme-color-text;
+ }
+
+ > .card-content {
+ .card-table {
+ margin: 16px 0 16px -20px;
+ box-shadow: 0 0;
+ width: ~"calc(100% + 40px)";
+ border-radius: 0;
+ }
}
+
+ .card-content {
+ color: @theme-color-text;
+ .card-title {
+ margin-bottom: 16px;
+ display: block;
+ font-weight: 400;
+ }
+ }
+}
+
+.card-table + .tableActionBar {
+ margin: 0 0 0 -20px;
} \ No newline at end of file
diff --git a/plugins/Morpheus/stylesheets/ui/_charts.less b/plugins/Morpheus/stylesheets/ui/_charts.less
index e475ad8f7a..6a3361b7e9 100644
--- a/plugins/Morpheus/stylesheets/ui/_charts.less
+++ b/plugins/Morpheus/stylesheets/ui/_charts.less
@@ -1,6 +1,6 @@
// bar graph colors
.bar-graph-colors[data-name=grid-background] {
- color: @theme-color-background-base;
+ color: @theme-color-background-contrast;
}
.bar-graph-colors[data-name=grid-border] {
@@ -53,7 +53,7 @@
// pie graph colors
.pie-graph-colors[data-name=grid-background] {
- color: @theme-color-background-base;
+ color: @theme-color-background-contrast;
}
.pie-graph-colors[data-name=grid-border] {
diff --git a/plugins/Morpheus/stylesheets/ui/_code.less b/plugins/Morpheus/stylesheets/ui/_code.less
index 149ec8240c..4bfd9d2cda 100644
--- a/plugins/Morpheus/stylesheets/ui/_code.less
+++ b/plugins/Morpheus/stylesheets/ui/_code.less
@@ -14,7 +14,10 @@ pre, .codeblock {
border: none;
border-radius: 3px;
direction: ltr;
- margin: 0 0 15px;
+ margin: 15px 0;
padding: 20px;
text-align: left;
+ height: auto;
+ display: block;
+ width: 100%;
}
diff --git a/plugins/Morpheus/stylesheets/ui/_components.less b/plugins/Morpheus/stylesheets/ui/_components.less
index 630fda6fa6..c8ff4a3e70 100644
--- a/plugins/Morpheus/stylesheets/ui/_components.less
+++ b/plugins/Morpheus/stylesheets/ui/_components.less
@@ -1,5 +1,5 @@
//colors calendar
-@calendarHeaderBackground: @theme-color-background-base;
+@calendarHeaderBackground: @theme-color-background-contrast;
@calendarHeaderColor: #999;
@calendarCurrentStateHover: #f5f5f5;
@calendarBorder: #ccc;
@@ -46,6 +46,7 @@
background: @color-white;
border-color: @color-silver-l80;
line-height: 1.33;
+ box-shadow: 4px 7px 25px rgba(0,0,0,0.3);
.segment-add-row {
.border-radius(5px);
@@ -53,7 +54,7 @@
.custom_select_search {
input {
- margin-top: 1px;
+ margin-top: 4px;
}
}
@@ -74,7 +75,7 @@
select, input {
.font-default(12px, 14px);
color: @theme-color-text;
- font-weight: 500;
+ font-weight: 600;
margin: 0;
height: 30px;
}
@@ -151,12 +152,20 @@
.font-default(10px, 12px);
}
-#periodString {
- select {
+.ui-datepicker {
+ .ui-datepicker-month,
+ .ui-datepicker-year {
min-height: 0;
background-position: 140%;
padding-left: 5px;
+ display: inline-block;
+ color: #666;
+ border: 0;
+ height: 19px;
+ line-height: 19px;
}
+}
+#periodString {
label.selected-period-label {
text-decoration: none !important;
@@ -184,8 +193,11 @@
&.isPiwikDemo {
text-align: right;
position: absolute;
- right: 0;
+ right: 16px;
top: 8px;
+ .dropdown {
+ min-width: 280px;
+ }
}
}
@@ -242,7 +254,7 @@
margin: 27px 0 0 319px;
width: 258px;
height: 390px;
- background: @theme-color-background-base;
+ background: @theme-color-background-contrast;
h2 {
color: #1e93d1;
border-bottom: 1px solid @color-gray;
@@ -295,8 +307,8 @@
table.dataTable tr td .dataTableRowActions {
a.rightmost, a {
- margin: 6px 0px 6px 0;
- padding: 0px 4px 0px 0px;
+ margin: 6px 0 6px 0;
+ padding: 0 4px 0 0;
}
}
diff --git a/plugins/Morpheus/stylesheets/ui/_tabs.less b/plugins/Morpheus/stylesheets/ui/_tabs.less
new file mode 100644
index 0000000000..8a8a127a5d
--- /dev/null
+++ b/plugins/Morpheus/stylesheets/ui/_tabs.less
@@ -0,0 +1,23 @@
+.row {
+ .tabs {
+ .indicator {
+ background-color: @theme-color-link;
+ }
+ .tab {
+ a {
+ &:hover {
+ color: @theme-color-link;
+ opacity: 0.7;
+ }
+ color: @theme-color-link;
+ }
+ }
+ }
+ .tab-content {
+ padding: 24px 24px 16px 0 !important;
+ a {
+ color: @theme-color-link;
+ text-decoration: none;
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/Morpheus/stylesheets/uibase/_header.less b/plugins/Morpheus/stylesheets/uibase/_header.less
index ff7fd45ff2..e1d99c3028 100644
--- a/plugins/Morpheus/stylesheets/uibase/_header.less
+++ b/plugins/Morpheus/stylesheets/uibase/_header.less
@@ -1,29 +1,22 @@
#root {
#logo {
- padding: 9px 0 0 4px;
- float: left;
- }
-
- #logo {
- img.default-piwik-logo {
- width: 82px;
- margin-top: 8px;
- }
+ padding-left: 16px;
+ padding-top: 4px;
img {
max-height: 30px;
+
+ &.default-piwik-logo {
+ width: 95px;
+ }
}
+
}
#logo > a {
text-decoration: none;
}
-
- #logo {
- // IE 9 only
- width: 200px\9\0;
- }
}
// IE 10+
diff --git a/plugins/Morpheus/stylesheets/uibase/_headerMessage.less b/plugins/Morpheus/stylesheets/uibase/_headerMessage.less
index 6e8cd0cf89..9afb25999f 100644
--- a/plugins/Morpheus/stylesheets/uibase/_headerMessage.less
+++ b/plugins/Morpheus/stylesheets/uibase/_headerMessage.less
@@ -3,7 +3,6 @@
z-index: 0;
cursor: default;
float: right;
- overflow: hidden;
display: block;
height: 20px;
font-size: 14px;
@@ -26,6 +25,10 @@
z-index: 150;
}
+#header_message .dropdown {
+ font-size: 12px;
+}
+
#header_message .header_short {
display: block;
white-space: nowrap;
diff --git a/plugins/Morpheus/stylesheets/uibase/_languageSelect.less b/plugins/Morpheus/stylesheets/uibase/_languageSelect.less
index 6161506aa8..ef2b23bf3b 100644
--- a/plugins/Morpheus/stylesheets/uibase/_languageSelect.less
+++ b/plugins/Morpheus/stylesheets/uibase/_languageSelect.less
@@ -1,5 +1,6 @@
.languageSelection {
- padding-right: 11px;
+ padding: 0 15px;
+ display: inline-block;
.items {
margin-left: -50px;
@@ -9,16 +10,3 @@
transition: box-shadow 0s !important;
}
}
-
-#topRightBar .navbar-right .languageSelection {
- // make padding of language selection clickable
- margin: -14px -12px;
-
- .title {
- padding: 14px 12px;
- &:after {
- top: 19px;
- right: -3px;
- }
- }
-} \ No newline at end of file
diff --git a/plugins/Morpheus/stylesheets/uibase/_periodSelect.less b/plugins/Morpheus/stylesheets/uibase/_periodSelect.less
index 83c21f7999..95cad2a3c3 100644
--- a/plugins/Morpheus/stylesheets/uibase/_periodSelect.less
+++ b/plugins/Morpheus/stylesheets/uibase/_periodSelect.less
@@ -10,11 +10,32 @@
background-color: #f7f7f7;
}
+#ajaxLoadingCalendar {
+ white-space: nowrap;
+}
+
#periodString:hover {
background-color: #f1f0eb;
border-color: #a9a399;
}
+#periodString td {
+ vertical-align: top;
+ border-radius: 0
+}
+
+#periodString {
+ .ui-widget-header,
+ .ui-datepicker {
+ &.ui-helper-clearfix:before, &.ui-helper-clearfix:after {
+ display: inline;
+ clear: none;
+ content: none !important;
+ }
+ }
+
+}
+
#periodString .calendar-icon {
width: 13px;
height: 15px;
@@ -41,24 +62,15 @@
#periodString .period-date,
#periodString .period-range {
- float: left;
padding: 0 16px 0 0;
}
#periodString .period-type {
- float: left;
padding: 0;
}
#periodString .period-type label {
- font-size: 12px;
- display: inline-block;
- padding: 2px 0 3px 0;
- vertical-align: top;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
+ font-size: 13px;
}
#periodString label.selected-period-label {
diff --git a/plugins/Morpheus/templates/admin.twig b/plugins/Morpheus/templates/admin.twig
index fac1ce7987..73a898a732 100644
--- a/plugins/Morpheus/templates/admin.twig
+++ b/plugins/Morpheus/templates/admin.twig
@@ -6,19 +6,23 @@
{% set isAdminArea = true %}
{% block body %}
- {% if userIsAnonymous %}
- {% set topMenuModule = 'ScheduledReports' %}
- {% set topMenuAction = 'index' %}
- {% else %}
- {% set topMenuModule = 'UsersManager' %}
- {% set topMenuAction = 'userSettings' %}
- {% endif %}
+ {% set topMenuModule = 'CoreAdminHome' %}
+ {% set topMenuAction = 'home' %}
{{ parent() }}
{% endblock %}
{% block root %}
{% include "@CoreHome/_topScreen.twig" %}
+ <div class="top_controls">
+ <div piwik-quick-access ng-cloak class="piwikTopControl borderedControl"></div>
+
+ {% block topcontrols %}
+ {% endblock %}
+
+ {% include "@CoreHome/_headerMessage.twig" %}
+ </div>
+
{% import 'ajaxMacros.twig' as ajax %}
{{ ajax.requestErrorDiv(emailSuperUser|default(''), arePiwikProAdsEnabled) }}
{{ postEvent("Template.beforeContent", "admin", currentModule) }}
@@ -31,17 +35,13 @@
{% endif %}
<div class="pageWrap">
-
- <div class="top_controls">
- {% block topcontrols %}
- {% endblock %}
-
- {% include "@CoreHome/_headerMessage.twig" %}
- </div>
-
- <div class="admin" id="content">
+ <a name="main"></a>
+ {% block notification %}
{% include "@CoreHome/_notifications.twig" %}
- {% include "@CoreHome/_warningInvalidHost.twig" %}
+ {% endblock %}
+ {% include "@CoreHome/_warningInvalidHost.twig" %}
+
+ <div class="admin" id="content" ng-cloak>
<div class="ui-confirm" id="alert">
<h2></h2>
diff --git a/plugins/Morpheus/templates/contentBlock.twig b/plugins/Morpheus/templates/contentBlock.twig
new file mode 100644
index 0000000000..b31e9bd1c6
--- /dev/null
+++ b/plugins/Morpheus/templates/contentBlock.twig
@@ -0,0 +1,13 @@
+<div class="card">
+ <div class="card-content">
+ {% if title is defined and title %}
+ <h2 class="card-title"
+ {% if rate is defined and rate and rate is true %}piwik-enriched-headline="{{ title|e('html_attr') }}"
+ {% elseif rate is defined and rate and rate %}piwik-enriched-headline="{{ rate|e('html_attr') }}"{% endif %}
+ >{% block helpText %}{% endblock %}{{ title }}</h2>
+ {% endif %}
+
+ {% block content %}
+ {% endblock %}
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/Morpheus/templates/dashboard.twig b/plugins/Morpheus/templates/dashboard.twig
index 8adf020f2c..9bb9bf19a7 100644
--- a/plugins/Morpheus/templates/dashboard.twig
+++ b/plugins/Morpheus/templates/dashboard.twig
@@ -23,6 +23,13 @@
{% include "@CoreHome/_warningInvalidHost.twig" %}
{% include "@CoreHome/_topScreen.twig" %}
+ <div class="top_controls">
+ <div piwik-quick-access ng-cloak class="piwikTopControl borderedControl"></div>
+
+ {% block topcontrols %}
+ {% endblock %}
+ </div>
+
<div class="ui-confirm" id="alert">
<h2></h2>
<input role="yes" type="button" value="{{ 'General_Ok'|translate }}"/>
@@ -33,21 +40,12 @@
<div class="page">
{% if showMenu is defined and showMenu %}
- <div id="secondNavBar" class="Menu--dashboard">
- <div id="search">
- <div piwik-quick-access class="borderedControl"></div>
- </div>
+ <div id="secondNavBar" class="Menu--dashboard z-depth-1">
<div piwik-reporting-menu></div>
</div>
{% endif %}
- <div class="pageWrap">
-
-
- <div class="top_controls">
- {% block topcontrols %}
- {% endblock %}
- </div>
+ <div class="pageWrap" ng-cloak>
<a name="main"></a>
{% block notification %}
diff --git a/plugins/Morpheus/templates/demo.twig b/plugins/Morpheus/templates/demo.twig
index 51cdc9e86f..4b807b8eb7 100644
--- a/plugins/Morpheus/templates/demo.twig
+++ b/plugins/Morpheus/templates/demo.twig
@@ -69,53 +69,41 @@
This page is intended to show all the UI components and styles available to use in Piwik.
</p>
- <h2>Containers</h2>
- <p>
- All HTML should be in a container:
- </p>
- <ul>
- <li><code>.container-fluid</code>: this container is fluid and will take 100% of the width</li>
- <li><code>.container</code>: this container has a max-width that adapts to the screen size</li>
- </ul>
- <p>
- Read more about <a href="http://getbootstrap.com/css/#overview-container">Bootstrap's containers</a>.
- </p>
-
<h2>Grid system</h2>
<p>
- Bootstrap's grid system allows to organize the content in columns, all the while staying responsive for smaller screens.
+ Materialize's grid system allows to organize the content in columns, all the while staying responsive for smaller screens.
</p>
<div class="grid-demo">
<div class="row">
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
- <div class="col-md-1">.col-md-1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
+ <div class="col m1">.col m1</div>
</div>
<div class="row">
- <div class="col-md-8">.col-md-8</div>
- <div class="col-md-4">.col-md-4</div>
+ <div class="col s8">.col m8</div>
+ <div class="col m4">.col m4</div>
</div>
<div class="row">
- <div class="col-md-4">.col-md-4</div>
- <div class="col-md-4">.col-md-4</div>
- <div class="col-md-4">.col-md-4</div>
+ <div class="col m4">.col m4</div>
+ <div class="col m4">.col m4</div>
+ <div class="col m4">.col m4</div>
</div>
<div class="row">
- <div class="col-md-6">.col-md-6</div>
- <div class="col-md-6">.col-md-6</div>
+ <div class="col m6">.col m6</div>
+ <div class="col m6">.col m6</div>
</div>
</div>
<p>
- Read more about <a href="http://getbootstrap.com/css/#grid">Bootstrap's grid system</a>.
+ Read more about <a href="http://materializecss.com/grid.html">Materialize's grid system</a>.
</p>
<h2>Typography</h2>
@@ -138,14 +126,25 @@
<h3>Alignment classes</h3>
<div class="demo">
- <p class="text-left">Left aligned text.</p>
- <p class="text-center">Center aligned text.</p>
- <p class="text-right">Right aligned text.</p>
+ <p class="left">Left aligned block.</p>
+ <p class="center">Center aligned block.</p>
+ <p class="right">Right aligned block.</p>
</div>
<div class="demo-code">
- <pre>&lt;p class=&quot;text-left&quot;&gt;Left aligned text.&lt;/p&gt;
-&lt;p class=&quot;text-center&quot;&gt;Center aligned text.&lt;/p&gt;
-&lt;p class=&quot;text-right&quot;&gt;Right aligned text.&lt;/p&gt;</pre>
+ <pre>&lt;p class=&quot;left&quot;&gt;Left aligned block.&lt;/p&gt;
+&lt;p class=&quot;center&quot;&gt;Center aligned block.&lt;/p&gt;
+&lt;p class=&quot;right&quot;&gt;Right aligned block.&lt;/p&gt;</pre>
+ </div>
+
+ <div class="demo">
+ <p class="left-align">Left aligned text.</p>
+ <p class="center-align">Center aligned text.</p>
+ <p class="right-align">Right aligned text.</p>
+ </div>
+ <div class="demo-code">
+ <pre>&lt;p class=&quot;left-align&quot;&gt;Left aligned text.&lt;/p&gt;
+&lt;p class=&quot;center-align&quot;&gt;Center aligned text.&lt;/p&gt;
+&lt;p class=&quot;right-align&quot;&gt;Right aligned text.&lt;/p&gt;</pre>
</div>
<h3>Lists</h3>
@@ -167,235 +166,308 @@
<div class="demo">
<a class="btn" href="#">Link</a>
<input type="button" class="btn" value="Input">
- <input type="submit" value="Submit">
- <button type="submit">Button</button>
+ <input type="submit" class="btn" value="Submit">
</div>
<div class="demo-code">
<pre>&lt;a class=&quot;btn&quot; href=&quot;#&quot;&gt;Link&lt;/a&gt;
&lt;input type=&quot;button&quot; class=&quot;btn&quot; value=&quot;Input&quot;&gt;
-&lt;input type=&quot;submit&quot; value=&quot;Submit&quot;&gt;
-&lt;button type=&quot;submit&quot;&gt;Button&lt;/button&gt;</pre>
+&lt;input type=&quot;submit&quot; class=&quot;brn&quot; value=&quot;Submit&quot;&gt;</pre>
</div>
<div class="demo">
- <button class="btn btn-lg">Large button</button>
- <a class="btn btn-lg" href="#">Large button</a>
+ <button class="btn btn-large">Large button</button>
+ <a class="btn btn-large" href="#">Large button</a>
</div>
<div class="demo-code">
- <pre>&lt;button class=&quot;btn btn-lg&quot;&gt;Large button&lt;/button&gt;
-&lt;a class=&quot;btn btn-lg&quot; href=&quot;#&quot;&gt;Large button&lt;/a&gt;</pre>
+ <pre>&lt;button class=&quot;btn btn-large&quot;&gt;Large button&lt;/button&gt;
+&lt;a class=&quot;btn btn-large&quot; href=&quot;#&quot;&gt;Large button&lt;/a&gt;</pre>
</div>
<div class="demo">
<div class="div-block">
<a class="btn btn-block" href="#">Block button</a>
- <a class="btn btn-lg btn-block" href="#">Large block button</a>
+ <a class="btn btn-large btn-block" href="#">Large block button</a>
</div>
</div>
<div class="demo-code">
<pre>&lt;a class=&quot;btn btn-block&quot; href=&quot;#&quot;&gt;Block button&lt;/a&gt;
-&lt;a class=&quot;btn btn-lg btn-block&quot; href=&quot;#&quot;&gt;Large block button&lt;/a&gt;</pre>
+&lt;a class=&quot;btn btn-large btn-block&quot; href=&quot;#&quot;&gt;Large block button&lt;/a&gt;</pre>
</div>
- <h3>Link buttons</h3>
+ <h3>Flat buttons</h3>
<div class="demo">
- <a class="btn btn-link" href="#">Link button</a>
+ <a class="btn-flat" href="#">Flat button</a>
+ <a class="btn-flat" href="#"><span class="icon-add"></span></a>
+ <a class="btn-flat btn-large" href="#">Flat button</a>
+ <a class="btn-flat btn-large" href="#"><span class="icon-add"></span></a>
</div>
<div class="demo-code">
- <pre>&lt;a class=&quot;btn btn-link&quot; href=&quot;#&quot;&gt;Flat button&lt;/a&gt;</pre>
+ <pre>&lt;a class=&quot;btn-flat&quot; href=&quot;#&quot;&gt;Flat button&lt;/a&gt;
+&lt;a class=&quot;btn-flat&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;icon-add&quot;&gt;&lt;/span&gt;&lt;/a&gt;
+&lt;a class=&quot;btn-flat btn-large&quot; href=&quot;#&quot;&gt;Flat button&lt;/a&gt;
+&lt;a class=&quot;btn-flat btn-large&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;icon-add&quot;&gt;&lt;/span&gt;&lt;/a&gt;</pre>
</div>
- <h3>Flat buttons</h3>
+ <h2>Loading indicator</h2>
+ <div class="demo">
+ <div piwik-activity-indicator loading="true"></div>
+ </div>
+ <div class="demo-code">
+ <pre>&lt;div piwik-activity-indicator loading=&quot;true&quot;>&lt;/div></pre>
+ </div>
+ <p>
+ This is an angular component. You can change the loading state via angular.
+ </p>
+ <h2>Progressbar</h2>
<div class="demo">
- <a class="btn btn-flat" href="#">Flat button</a>
- <a class="btn btn-flat" href="#"><span class="icon-add"></span></a>
- <a class="btn btn-flat btn-lg" href="#">Flat button</a>
- <a class="btn btn-flat btn-lg" href="#"><span class="icon-add"></span></a>
+ <div piwik-progressbar progress="5" label="'Downloading database'"></div>
</div>
<div class="demo-code">
- <pre>&lt;a class=&quot;btn btn-flat&quot; href=&quot;#&quot;&gt;Flat button&lt;/a&gt;
-&lt;a class=&quot;btn btn-flat&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;icon-add&quot;&gt;&lt;/span&gt;&lt;/a&gt;
-&lt;a class=&quot;btn btn-flat btn-lg&quot; href=&quot;#&quot;&gt;Flat button&lt;/a&gt;
-&lt;a class=&quot;btn btn-flat btn-lg&quot; href=&quot;#&quot;&gt;&lt;span class=&quot;icon-add&quot;&gt;&lt;/span&gt;&lt;/a&gt;</pre>
+ <pre>&lt;div piwik-progressbar progress=&quot;5&quot; label=&quot;Downloading database&quot;>&lt;/div></pre>
</div>
+ <p>
+ This is an angular component. Update the progress and label via angular.
+ </p>
<h2>Alerts</h2>
<div class="demo">
- <div class="alert alert-info">
+ <div piwik-alert="info">
<strong>Info!</strong> This alert needs <a>your attention</a>, but it's not super important.
</div>
- <div class="alert alert-success">
+ <div piwik-alert="success">
<strong>Success!</strong> You successfully read this important <a>alert message</a>.
</div>
- <div class="alert alert-warning">
+ <div piwik-alert="warning">
<strong>Warning!</strong> Better <a>check</a> yourself, you're not looking too good.
</div>
- <div class="alert alert-danger">
+ <div piwik-alert="danger">
<strong>Error!</strong> Change <a>a few things</a> up and try submitting again.
</div>
</div>
<div class="demo-code">
- <pre>&lt;div class=&quot;alert alert-info&quot;&gt;...&lt;/div&gt;
-&lt;div class=&quot;alert alert-success&quot;&gt;...&lt;/div&gt;
-&lt;div class=&quot;alert alert-warning&quot;&gt;...&lt;/div&gt;
-&lt;div class=&quot;alert alert-danger&quot;&gt;...&lt;/div&gt;</pre>
+ <pre>&lt;div piwik-alert=&quot;info&quot;&gt;
+ &lt;strong&gt;Info!&lt;/strong&gt; This alert needs &lt;a&gt;your attention&lt;/a&gt;, but it's not super important.
+&lt;/div&gt;
+&lt;div piwik-alert=&quot;success&quot;&gt;
+ &lt;strong&gt;Success!&lt;/strong&gt; You successfully read this important &lt;a&gt;alert message&lt;/a&gt;.
+&lt;/div&gt;
+&lt;div piwik-alert=&quot;warning&quot;&gt;
+ &lt;strong&gt;Warning!&lt;/strong&gt; Better &lt;a&gt;check&lt;/a&gt; yourself, you're not looking too good.
+&lt;/div&gt;
+&lt;div piwik-alert=&quot;danger&quot;&gt;
+ &lt;strong&gt;Error!&lt;/strong&gt; Change &lt;a&gt;a few things&lt;/a&gt; up and try submitting again.
+&lt;/div&gt;</pre>
</div>
- <p>
- Read more about <a href="http://getbootstrap.com/components/#alerts">Bootstrap's alerts</a>.
- </p>
-
<h2>Forms</h2>
<h3>Simple form</h3>
<div class="demo">
- <form action="#">
- <div class="form-group">
- <label for="username">
- Username
- </label>
- <input type="text" id="username" placeholder="Some text here..."/>
+ <div piwik-form>
+ <div piwik-field uicontrol="text" name="username"
+ title="Username"
+ introduction="This is an introduction. It can be used to group form fields"
+ placeholder="Some text here">
</div>
- <div class="form-group">
- <label for="email">
- Email
- </label>
- <div class="form-help">
- Here is more information.
- </div>
- <input type="email" id="email" placeholder="Some email here..."/>
+ <div piwik-field uicontrol="text" name="email"
+ title="Email"
+ inline-help="This is the inline help which provides more information.">
</div>
- <div class="form-group">
- <label>
- Report to load by default
- </label>
- <div class="form-help">
- This is a help text that can be used to describe the field.
- This help text may extend over several lines.
- </div>
- <label class="radio">
- <input type="radio" name="defaultReport" value="today">
- Today
- </label>
- <label class="radio">
- <input type="radio" name="defaultReport" value="yesterday">
- Yesterday
- </label>
- <label class="radio">
- <input type="radio" name="defaultReport" value="week">
- Previous 30 days (not including today)
- </label>
+ <div piwik-field uicontrol="text" name="textWithoutPlaceholder"
+ title="This field has a title but no place holder">
</div>
- <div class="form-group">
- <label for="language">
- Language
- </label>
- <select id="language">
- <option>English</option>
- </select>
+ <div piwik-field uicontrol="text" name="textWithoutTitle"
+ title="This field has a place holder but no title">
</div>
- <div class="form-group">
- <label for="description">
- Description
- </label>
- <textarea id="description" rows="5"></textarea>
+ <div piwik-field uicontrol="text" name="textWithValue"
+ value="My value"
+ title="This field has already a value set">
</div>
- <input type="submit" value="Submit">
- </form>
+ <div piwik-field uicontrol="password" name="password"
+ title="Password"
+ placeholder="Enter your password here">
+ </div>
+
+ <div id="complexHelpText" class="inline-help-node">
+ It is possible to use all kind of HTML in the help text, including <a href="javascript:;">links</a>.
+ </div>
+
+ <div piwik-field uicontrol="text" name="alias"
+ title="Disabeld text field"
+ disabled="true"
+ placeholder="This value cannot be changed"
+ inline-help="#complexHelpText">
+ </div>
+
+ <div piwik-field uicontrol="text" name="fullWidthText"
+ title="Form fields can be made full witdth"
+ full-width="true"
+ placeholder="Some text here...">
+ </div>
+
+ <div piwik-field uicontrol="textarea" name="description"
+ title="Description"
+ inline-help="This is a textarea. It automatically gets larger the more text is entered.">
+ </div>
+
+ <div piwik-field uicontrol="select" name="language"
+ title="Language"
+ introduction="Select fields"
+ value="1"
+ inline-help="Single select"
+ options='{1: "English",2:"Spanish"}'>
+ </div>
+
+ <div piwik-field uicontrol="multiselect" name="phonenumbers"
+ title="Phone numbers"
+ value="1"
+ inline-help="Multi select"
+ options='{1: "0123456789",2:"9876543210",3:"5432109876"}'>
+ </div>
+
+ <div piwik-field uicontrol="checkbox" name="enableFeature"
+ title="Enable feature"
+ introduction="Radio and checkboxes"
+ inline-help="This is a single checkbox">
+ </div>
+
+ <div piwik-field uicontrol="checkbox" name="enableFeature"
+ title="Enable feature"
+ var-type="array"
+ options='{today: "Today", yesterday: "Yesterday",week: "Previous 30 days (not including today)"}'
+ inline-help="This field shows multiple checkboxes as we declare we want to get an array of values.">
+ </div>
+
+ <div piwik-field uicontrol="radio" name="defaultReportDate"
+ title="Report to load by default"
+ options='{today: "Today", yesterday: "Yesterday",week: "Previous 30 days (not including today)"}'
+ inline-help="This is a help text that can be used to describe the field. This help text may extend over several lines.">
+ </div>
+
+ <div piwik-field uicontrol="site" name="currentsite"
+ introduction="Piwik specific form fields"
+ title="Select a website">
+ </div>
+
+ <div piwik-save-button></div>
+ <div piwik-save-button
+ onconfirm="myController.save()"
+ disabled="myController.isDisabled"
+ value="Changed button text"
+ saving="myController.isLoading">
+ </div>
+ </div>
</div>
<div class="demo-code">
- <pre>&lt;form action=&quot;#&quot;&gt;
- &lt;div class=&quot;form-group&quot;&gt;
- &lt;label for=&quot;username&quot;&gt;
- Username
- &lt;/label&gt;
- &lt;input type=&quot;text&quot; id=&quot;username&quot; placeholder=&quot;Some text here...&quot;/&gt;
+ <pre>&lt;div piwik-form&gt;
+ &lt;div piwik-field uicontrol=&quot;text&quot; name=&quot;username&quot;
+ title=&quot;Username&quot;
+ introduction=&quot;This is an introduction. It can be used to group form fields&quot;
+ placeholder=&quot;Some text here&quot;&gt;
&lt;/div&gt;
- &lt;div class=&quot;form-group&quot;&gt;
- &lt;label for=&quot;email&quot;&gt;
- Email
- &lt;/label&gt;
- &lt;div class=&quot;form-help&quot;&gt;
- Here is more information.
- &lt;/div&gt;
- &lt;input type=&quot;email&quot; id=&quot;email&quot; placeholder=&quot;Some email here...&quot;/&gt;
+ &lt;div piwik-field uicontrol=&quot;text&quot; name=&quot;email&quot;
+ title=&quot;Email&quot;
+ inline-help=&quot;This is the inline help which provides more information.&quot;&gt;
&lt;/div&gt;
- &lt;div class=&quot;form-group&quot;&gt;
- &lt;label&gt;
- Report to load by default
- &lt;/label&gt;
- &lt;div class=&quot;form-help&quot;&gt;
- This is a help text that can be used to describe the field.
- This help text may extend over several lines.
- &lt;/div&gt;
- &lt;label class=&quot;radio&quot;&gt;
- &lt;input type=&quot;radio&quot; name=&quot;defaultReport&quot; value=&quot;today&quot;&gt;
- Today
- &lt;/label&gt;
- &lt;label class=&quot;radio&quot;&gt;
- &lt;input type=&quot;radio&quot; name=&quot;defaultReport&quot; value=&quot;yesterday&quot;&gt;
- Yesterday
- &lt;/label&gt;
- &lt;label class=&quot;radio&quot;&gt;
- &lt;input type=&quot;radio&quot; name=&quot;defaultReport&quot; value=&quot;week&quot;&gt;
- Previous 30 days (not including today)
- &lt;/label&gt;
+ &lt;div piwik-field uicontrol=&quot;text&quot; name=&quot;textWithoutPlaceholder&quot;
+ title=&quot;This field has a title but no place holder&quot;&gt;
&lt;/div&gt;
- &lt;div class=&quot;form-group&quot;&gt;
- &lt;label for=&quot;language&quot;&gt;
- Language
- &lt;/label&gt;
- &lt;select id=&quot;language&quot;&gt;
- &lt;option&gt;English&lt;/option&gt;
- &lt;/select&gt;
+ &lt;div piwik-field uicontrol=&quot;text&quot; name=&quot;textWithoutTitle&quot;
+ title=&quot;This field has a place holder but no title&quot;&gt;
&lt;/div&gt;
- &lt;div class=&quot;form-group&quot;&gt;
- &lt;label for=&quot;description&quot;&gt;
- Description
- &lt;/label&gt;
- &lt;textarea id=&quot;description&quot; rows=&quot;5&quot;&gt;&lt;/textarea&gt;
+ &lt;div piwik-field uicontrol=&quot;text&quot; name=&quot;textWithValue&quot;
+ value=&quot;My value&quot;
+ title=&quot;This field has already a value set&quot;&gt;
&lt;/div&gt;
- &lt;input type=&quot;submit&quot; value=&quot;Submit&quot;&gt;
-&lt;/form&gt;</pre>
- </div>
+ &lt;div piwik-field uicontrol=&quot;password&quot; name=&quot;password&quot;
+ title=&quot;Password&quot;
+ placeholder=&quot;Enter your password here&quot;&gt;
+ &lt;/div&gt;
- <h3>Input group</h3>
+ &lt;div id=&quot;complexHelpText&quot; class=&quot;inline-help-node&quot;&gt;
+ It is possible to use all kind of HTML in the help text, including &lt;a href=&quot;javascript:;&quot;&gt;links&lt;/a&gt;.
+ &lt;/div&gt;
- <div class="demo">
- <div class="form-group">
- <label for="deleteOlderThan">
- Input with a text addon
- </label>
- <div class="input-group">
- <input type="text" id="deleteOlderThan" value="180"/>
- <span class="input-group-addon">days</span>
- </div>
- </div>
- </div>
- <div class="demo-code">
- <pre>&lt;div class=&quot;form-group&quot;&gt;
- &lt;label for=&quot;foo&quot;&gt;
- Input with a text addon
- &lt;/label&gt;
- &lt;div class=&quot;input-group&quot;&gt;
- &lt;input type=&quot;text&quot; id=&quot;foo&quot; value=&quot;180&quot;/&gt;
- &lt;span class=&quot;input-group-addon&quot;&gt;days&lt;/span&gt;
+ &lt;div piwik-field uicontrol=&quot;text&quot; name=&quot;alias&quot;
+ title=&quot;Disabeld text field&quot;
+ disabled=&quot;true&quot;
+ placeholder=&quot;This value cannot be changed&quot;
+ inline-help=&quot;#complexHelpText&quot;&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;text&quot; name=&quot;fullWidthText&quot;
+ title=&quot;Form fields can be made full witdth&quot;
+ full-width=&quot;true&quot;
+ placeholder=&quot;Some text here...&quot;&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;textarea&quot; name=&quot;description&quot;
+ title=&quot;Description&quot;
+ inline-help=&quot;This is a textarea. It automatically gets larger the more text is entered.&quot;&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;select&quot; name=&quot;language&quot;
+ title=&quot;Language&quot;
+ introduction=&quot;Select fields&quot;
+ value=&quot;1&quot;
+ inline-help=&quot;Single select&quot;
+ options='{1: &quot;English&quot;,2:&quot;Spanish&quot;}'&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;multiselect&quot; name=&quot;phonenumbers&quot;
+ title=&quot;Phone numbers&quot;
+ value=&quot;1&quot;
+ inline-help=&quot;Multi select&quot;
+ options='{1: &quot;0123456789&quot;,2:&quot;9876543210&quot;,3:&quot;5432109876&quot;}'&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;checkbox&quot; name=&quot;enableFeature&quot;
+ title=&quot;Enable feature&quot;
+ introduction=&quot;Radio and checkboxes&quot;
+ inline-help=&quot;This is a single checkbox&quot;&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;checkbox&quot; name=&quot;enableFeature&quot;
+ title=&quot;Enable feature&quot;
+ var-type=&quot;array&quot;
+ options='{today: &quot;Today&quot;, yesterday: &quot;Yesterday&quot;,week: &quot;Previous 30 days (not including today)&quot;}'
+ inline-help=&quot;This field shows multiple checkboxes as we declare we want to get an array of values.&quot;&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;radio&quot; name=&quot;defaultReportDate&quot;
+ title=&quot;Report to load by default&quot;
+ options='{today: &quot;Today&quot;, yesterday: &quot;Yesterday&quot;,week: &quot;Previous 30 days (not including today)&quot;}'
+ inline-help=&quot;This is a help text that can be used to describe the field. This help text may extend over several lines.&quot;&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-field uicontrol=&quot;site&quot; name=&quot;currentsite&quot;
+ introduction=&quot;Piwik specific form fields&quot;
+ title=&quot;Select a website&quot;&gt;
+ &lt;/div&gt;
+
+ &lt;div piwik-save-button&gt;&lt;/div&gt;
+ &lt;div piwik-save-button
+ onconfirm=&quot;myController.save()&quot;
+ disabled=&quot;myController.isDisabled&quot;
+ value=&quot;Changed button text&quot;
+ saving=&quot;myController.isLoading&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;</pre>
+ <p>If you do not want to use one ouf our form fields we recommend to add the class <code>browser-default</code> to the input or select element.</p>
</div>
<h2>Code</h2>
@@ -404,24 +476,24 @@
<div class="demo">
<p>
- You can put code in a text using the <code>code</code> tag.
+ You can put code in a text using the <code piwik-select-on-focus>code</code> tag.
</p>
</div>
<div class="demo-code">
- <pre>You can put code in a text using the &lt;code&gt;code&lt;/code&gt; tag.</pre>
+ <pre>You can put code in a text using the &lt;code piwik-select-on-focus&gt;code&lt;/code&gt; tag.</pre>
</div>
<h3>Block</h3>
<div class="demo">
<p>Or you can display a code block:</p>
- <pre>&lt;!-- Piwik --&gt;
+ <pre piwik-select-on-focus>&lt;!-- Piwik --&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
&lt;/script&gt;
&lt;!-- End Piwik Code --&gt;</pre>
</div>
<div class="demo-code">
- &lt;pre&gt;...&lt;/pre&gt;
+ &lt;pre piwik-select-on-focus&gt;...&lt;/pre&gt;
</div>
<h2>Tables</h2>
@@ -455,7 +527,63 @@
</table>
</div>
<div class="demo-code">
- <pre>&lt;table&gt;...&lt;/table&gt;</pre>
+ <pre>&lt;table&gt;&lt;thead&gt;...&lt;/thead&gt;&lt;tbody&gt;...&lt;/tbody&gt;&lt;/table&gt;</pre>
+ </div>
+
+ <h2>Content intro</h2>
+
+ <div class="demo">
+ <div piwik-content-intro>
+ <h2>My headline</h2>
+ <p>My text goes is in here</p>
+ </div>
+ </div>
+ <div class="demo-code">
+ <pre>&lt;div piwik-content-intro&gt;
+ &lt;h2&gt;My headline&lt;/h2&gt;
+ &lt;p&gt;My text goes is in here&lt;/p&gt;
+&lt;/div&gt;</pre>
+ </div>
+ <p>A content intro can be used as an introduction to the following content and is usually used as the first part of a page followed by one or multiple content blocks.</p>
+
+ <h2>Content blocks</h2>
+
+ <div class="demo">
+ <div piwik-content-block content-title="My title" help-url="https://piwik.org">
+ <p>My text goes is in here</p>
+ </div>
+ </div>
+ <div class="demo-code">
+ <pre>&lt;div piwik-content-block content-title=&quot;My title&quot; help-url=&quot;https://piwik.org&quot;&gt;
+ &lt;p&gt;My text goes is in here&lt;/p&gt;
+&lt;/div&gt;</pre>
+ </div>
+
+ <h3>Content Table</h3>
+
+ <div class="demo">
+ <div piwik-content-block content-title="My title" help-url="https://piwik.org">
+ <p>My intro text is here</p>
+ <table piwik-content-table>
+ <thead><tr><th>Column 1</th><th>Column 2</th></tr></thead>
+ <tbody>
+ <tr><td>Value 1</td><td>Value 2</td></tr>
+ <tr><td>Value 1</td><td>Value 2</td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="demo-code">
+ <pre>&lt;div piwik-content-block content-title=&quot;My title&quot; help-url=&quot;https://piwik.org&quot;&gt;
+ &lt;p&gt;My intro text is here&lt;/p&gt;
+ &lt;table piwik-content-table&gt;
+ &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Column 1&lt;/th&gt;&lt;th&gt;Column 2&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
+ &lt;tbody&gt;
+ &lt;tr&gt;&lt;td&gt;Value 1&lt;/td&gt;&lt;td&gt;Value 2&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;td&gt;Value 1&lt;/td&gt;&lt;td&gt;Value 2&lt;/td&gt;&lt;/tr&gt;
+ &lt;/tbody&gt;
+ &lt;/table&gt;
+&lt;/div&gt;</pre>
</div>
<h2>Icons</h2>
@@ -585,7 +713,7 @@
var row = $('<div class="row"></div>');
icons[category].forEach(function(icon) {
icon = 'icon-' + icon;
- row.append('<div class="col-sm-4 icon"><span class="' + icon + '"></span> ' + icon + '</div>');
+ row.append('<div class="col s4 icon"><span class="' + icon + '"></span> ' + icon + '</div>');
});
iconsDiv.append(row);
}
diff --git a/plugins/Morpheus/templates/genericForm.twig b/plugins/Morpheus/templates/genericForm.twig
index 571931df6f..480740299b 100644
--- a/plugins/Morpheus/templates/genericForm.twig
+++ b/plugins/Morpheus/templates/genericForm.twig
@@ -14,22 +14,24 @@
{{ form_data.hidden|join|raw }}
{% for fieldname in element_list %}
- {% if form_data[fieldname] is defined %}
- <div class="form-group">
- {% if form_data[fieldname].type == 'checkbox' %}
- <label class="checkbox">
+ {% if form_data[fieldname] is defined %}
+ <div class="row form-group">
+ <div class="col s12 m12 l6">
+ {% if form_data[fieldname].type == 'checkbox' %}
+ <label class="checkbox">
+ {{ form_data[fieldname].html|raw }}
+ </label>
+ {% elseif form_data[fieldname].label %}
+ <label>
+ {{ form_data[fieldname].label|raw }}
+ </label>
{{ form_data[fieldname].html|raw }}
- </label>
- {% elseif form_data[fieldname].label %}
- <label>
- {{ form_data[fieldname].label|raw }}
- </label>
- {{ form_data[fieldname].html|raw }}
- {% elseif form_data[fieldname].type == 'hidden' %}
- {{ form_data[fieldname].html|raw }}
- {% endif %}
- </div>
- {% endif %}
+ {% elseif form_data[fieldname].type == 'hidden' %}
+ {{ form_data[fieldname].html|raw }}
+ {% endif %}
+ </div>
+ </div>
+ {% endif %}
{% endfor %}
{{ form_data.submit.html|raw }}
diff --git a/plugins/Morpheus/templates/layout.twig b/plugins/Morpheus/templates/layout.twig
index 2229ba2ac7..83b96d545e 100644
--- a/plugins/Morpheus/templates/layout.twig
+++ b/plugins/Morpheus/templates/layout.twig
@@ -23,15 +23,10 @@
{% include "@CoreHome/_favicon.twig" %}
{% include "_jsGlobalVariables.twig" %}
{% include "_jsCssIncludes.twig" %}
-
- <!--[if IE]>
- <link rel="stylesheet" type="text/css" href="plugins/Morpheus/stylesheets/ieonly.css"/>
- <![endif]-->
+ {%- if not isCustomLogo %}<link rel="manifest" href="plugins/CoreHome/javascripts/manifest.json">{% endif %}
{% endblock %}
</head>
- <!--[if (gte IE 9)|!(IE)]><!-->
<body id="{{ bodyId|default('') }}" ng-app="app" class="{{ bodyClass|default('') }}">
- <!--<![endif]-->
{% block body %}
diff --git a/plugins/Morpheus/templates/settingsMacros.twig b/plugins/Morpheus/templates/settingsMacros.twig
deleted file mode 100644
index c09aa608eb..0000000000
--- a/plugins/Morpheus/templates/settingsMacros.twig
+++ /dev/null
@@ -1,137 +0,0 @@
-{% macro singleSetting(setting, index = 0) %}
-
- {% set settingValue = setting.getValue %}
-
- <div class="form-group">
-
- {% if setting.introduction %}
- <p class="settingIntroduction">{{ setting.introduction }}</p>
- {% endif %}
-
- {{ _self.field(setting, index) }}
-
- </div>
-
-{% endmacro %}
-
-{% macro field(setting, index = -1) %}
-
- {% if index == -1 %}
- {% set index = setting.getName %}
- {% endif %}
-
- {% set settingValue = setting.getValue %}
-
- {% if setting.uiControl != 'checkbox' %}
- <label>{{ setting.title }}</label>
- {% endif %}
-
- {% if setting.inlineHelp %}
- <div class="form-help">
- {{ setting.inlineHelp }}
- {% if setting.defaultValue and setting.uiControl != 'checkbox' and setting.uiControl != 'radio' %}
- <br/>
- {{ 'General_Default'|translate }}:
- {% if setting.defaultValue is iterable %}
- {{ setting.defaultValue|join(', ')|truncate(50) }}
- {% else %}
- {{ setting.defaultValue|truncate(50) }}
- {% endif %}
- {% endif %}
- </div>
- {% endif %}
-
- {% if setting.uiControl == 'select' or setting.uiControl == 'multiselect' %}
- <select
- {% for attr, val in setting.uiControlAttributes %}
- {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}"
- {% endfor %}
- name="{{ setting.getKey|e('html_attr') }}"
- {% if setting.uiControl == 'multiselect' %}multiple{% endif %}>
-
- {% for key, value in setting.availableValues %}
- <option value='{{ key }}'
- {% if settingValue is iterable and key in settingValue %}
- selected='selected'
- {% elseif settingValue==key %}
- selected='selected'
- {% endif %}>
- {{ value }}
- </option>
- {% endfor %}
-
- </select>
- {% elseif setting.uiControl == 'textarea' %}
- <textarea style="width: 376px; height: 250px;"
- {% for attr, val in setting.uiControlAttributes %}
- {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}"
- {% endfor %}
- name="{{ setting.getKey|e('html_attr') }}"
- >
- {{- settingValue -}}
- </textarea>
- {% elseif setting.uiControl == 'radio' %}
-
- {% for key, value in setting.availableValues %}
- <label class="radio">
- <input
- id="name-value-{{ index }}"
- {% for attr, val in setting.uiControlAttributes %}
- {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}"
- {% endfor %}
- {% if settingValue is sameas(key) %}
- checked="checked"
- {% endif %}
- type="radio"
- value="{{ key|e('html_attr') }}"
- name="{{ setting.getKey|e('html_attr') }}" />
-
- {{ value }}
-
- {% if setting.description %}
- <span class='form-description'>{{ setting.description }}</span>
- {% endif %}
-
- </label>
- {% endfor %}
-
- {% elseif setting.uiControl == 'checkbox' %}
-
- <label class="checkbox">
- <input id="name-value-{{ index }}"
- {% for attr, val in setting.uiControlAttributes %}
- {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}"
- {% endfor %}
- value="1"
- {% if settingValue %}
- checked="checked"
- {% endif %}
- type="checkbox"
- name="{{ setting.getKey|e('html_attr') }}">
-
- {{ setting.title }}
-
- {% if setting.description %}
- <span class='form-description'>{{ setting.description }}</span>
- {% endif %}
- </label>
-
- {% else %}
-
- <input
- {% for attr, val in setting.uiControlAttributes %}
- {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}"
- {% endfor %}
- class="control_{{ setting.uiControl|e('html_attr') }}"
- type="{{ setting.uiControl|e('html_attr') }}"
- name="{{ setting.getKey|e('html_attr') }}"
- value="{{ settingValue|e('html_attr') }}">
-
- {% endif %}
-
- {% if setting.uiControl != 'checkbox' and setting.uiControl != 'radio' %}
- <span class='form-description'>{{ setting.description }}</span>
- {% endif %}
-
-
-{% endmacro %}
diff --git a/plugins/MultiSites/angularjs/dashboard/dashboard-model.service.js b/plugins/MultiSites/angularjs/dashboard/dashboard-model.service.js
index d90d0f0aec..0cc399c464 100644
--- a/plugins/MultiSites/angularjs/dashboard/dashboard-model.service.js
+++ b/plugins/MultiSites/angularjs/dashboard/dashboard-model.service.js
@@ -24,6 +24,7 @@
lastVisits : '?',
lastVisitsDate : '?',
numberOfSites : 0,
+ loadingMessage: _pk_translate('MultiSites_LoadingWebsites'),
updateWebsitesList: updateWebsitesList,
getNumberOfFilteredSites: getNumberOfFilteredSites,
getNumberOfPages: getNumberOfPages,
diff --git a/plugins/MultiSites/angularjs/dashboard/dashboard.directive.html b/plugins/MultiSites/angularjs/dashboard/dashboard.directive.html
index 06a7fb6613..46bba85aad 100644
--- a/plugins/MultiSites/angularjs/dashboard/dashboard.directive.html
+++ b/plugins/MultiSites/angularjs/dashboard/dashboard.directive.html
@@ -1,5 +1,6 @@
<div>
<h2 piwik-enriched-headline
+ class="card-title"
help-url="http://piwik.org/docs/manage-websites/#all-websites-dashboard"
feature-name="{{ 'General_AllWebsitesDashboard'|translate }}">
{{ 'General_AllWebsitesDashboard'|translate }}
@@ -34,7 +35,7 @@
<span class="arrow" ng-class="{multisites_asc: !model.reverse && evolutionSelector == model.sortColumn, multisites_desc: model.reverse && evolutionSelector == model.sortColumn}"></span>
<span class="evolution"
ng-click="model.sortBy(evolutionSelector)"> {{ 'MultiSites_Evolution'|translate }}</span>
- <select class="selector native-select" id="evolution_selector" ng-model="evolutionSelector"
+ <select class="selector browser-default" id="evolution_selector" ng-model="evolutionSelector"
ng-change="model.sortBy(evolutionSelector)">
<option value="visits_evolution">{{ 'General_ColumnNbVisits'|translate }}</option>
<option value="pageviews_evolution">{{ 'General_ColumnPageviews'|translate }}</option>
@@ -47,8 +48,7 @@
<tbody id="tb" ng-if="model.isLoading">
<tr>
<td colspan="7" class="allWebsitesLoading">
- {{ 'MultiSites_LoadingWebsites' | translate }}
- <span class="allWebsitesLoadingIndicator"> </span>
+ <div piwik-activity-indicator loading-message="model.loadingMessage" loading="model.isLoading"></div>
</td>
</tr>
</tbody>
@@ -85,44 +85,41 @@
<tfoot>
- <tr ng-if="hasSuperUserAccess">
- <td colspan="8" class="add_new_site">
- <a href="{{ url }}?module=SitesManager&action=index&showaddsite=1&period={{ period }}&date={{ date }}&idSite={{ idSite }}">
- <img src='plugins/Morpheus/images/add.png' alt=""/> {{ 'SitesManager_AddSite'|translate }}
- </a>
- </td>
- </tr>
-
- <tr ng-if="!hasSuperUserAccess">
- <td colspan="8" class="empty-row">
- <br/>
- </td>
- </tr>
<tr>
<td colspan="8" class="paging" ng-hide="model.numberOfPages() <= 1">
- <span id="prev" class="previous" ng-hide="model.currentPage == 0" ng-click="model.previousPage()">
- <span style="cursor:pointer;">&#171; {{ 'General_Previous'|translate }}</span>
- </span>
- <span class="dataTablePages">
- <span id="counter">
- {{ 'General_Pagination'|translate:model.getCurrentPagingOffsetStart():model.getCurrentPagingOffsetEnd():model.getNumberOfFilteredSites() }}
- </span>
- </span>
- <span id="next" class="next" ng-hide="model.currentPage >= model.getNumberOfPages()" ng-click="model.nextPage()">
- <span style="cursor:pointer;" class="pointer">{{ 'General_Next'|translate }} &#187;</span>
- </span>
+ <div class="row">
+ <div class="col s3 add_new_site">
+ <a ng-if="hasSuperUserAccess" href="{{ url }}?module=SitesManager&action=index&showaddsite=1&period={{ period }}&date={{ date }}&idSite={{ idSite }}">
+ <span class="icon-add"></span> {{ 'SitesManager_AddSite'|translate }}
+ </a>
+ </div>
+ <div class="col s6">
+ <span id="prev" class="previous dataTablePrevious" ng-hide="model.currentPage == 0" ng-click="model.previousPage()">
+ <span style="cursor:pointer;">&#171; {{ 'General_Previous'|translate }}</span>
+ </span>
+ <span class="dataTablePages">
+ <span id="counter">
+ {{ 'General_Pagination'|translate:model.getCurrentPagingOffsetStart():model.getCurrentPagingOffsetEnd():model.getNumberOfFilteredSites() }}
+ </span>
+ </span>
+ <span id="next" class="next dataTableNext" ng-hide="model.currentPage >= model.getNumberOfPages()" ng-click="model.nextPage()">
+ <span style="cursor:pointer;" class="pointer">{{ 'General_Next'|translate }} &#187;</span>
+ </span>
+ </div>
+ <div class="col s3">&nbsp;</div>
+ </div>
</td>
</tr>
<tr row_id="last">
- <td colspan="8" class="site_search">
+ <td colspan="8" class="input-field site_search">
<input type="text"
ng-model="searchTerm"
+ class="browser-default"
piwik-onenter="model.searchSite(searchTerm)"
placeholder="{{ 'Actions_SubmenuSitesearch' | translate }}">
- <img title="{{ 'General_ClickToSearch' | translate }}"
+ <span title="{{ 'General_ClickToSearch' | translate }}"
ng-click="model.searchSite(searchTerm)"
- class="search_ico"
- src="plugins/Morpheus/images/search_ico.png"/>
+ class="icon-search search_ico"></span>
</td>
</tr>
diff --git a/plugins/MultiSites/angularjs/dashboard/dashboard.directive.less b/plugins/MultiSites/angularjs/dashboard/dashboard.directive.less
index c83ab162aa..4616cedbc2 100644
--- a/plugins/MultiSites/angularjs/dashboard/dashboard.directive.less
+++ b/plugins/MultiSites/angularjs/dashboard/dashboard.directive.less
@@ -10,14 +10,7 @@
}
#multisites {
- border: 0;
- padding: 0 15px;
- font-size: 14px;
-
- h2 {
- border-bottom: 0px;
- font-size: 24px;
- }
+ width: 80%;
.notification-error {
margin-top: 15px;
@@ -26,20 +19,29 @@
}
}
- .add_new_site,
- .clean {
+ .add_new_site {
border: 0 !important;
- text-align: right; padding-top: 15px;padding-right:10px;
- }
+ font-size: 13px;
+ text-align: left;
- .add_new_site {
- img {
- margin: 0px;
+ a {
+ color: @theme-color-text;
+ &:hover {
+ text-decoration: underline !important;
+ }
}
}
+ .clean {
+ border: 0 !important;
+ text-align: right;
+ padding-right:10px;
+ padding-top: 19px;
+ padding-bottom: 5px;
+ }
+
.site_search {
- padding: 0px;
+ padding: 0;
text-align: center;
border: 0 !important;
}
@@ -56,48 +58,37 @@
td, tr, .sparkline {
text-align: center;
vertical-align: middle;
- padding: 2px 6px 2px 12px;
- margin: 0;
- }
-
- th {
- padding: 12px 6px 12px 12px;
}
td.empty-row {
border-bottom: none !important;
}
- .indicator {
- background: url(plugins/Morpheus/images/loading-blue.gif) no-repeat center;
- height: 20px;
- width: 60px;
- margin: auto;
- border: 0 !important;
- }
-
.paging {
padding: 5px;
- font-size: 10px;
- border-bottom: 0px !important;
+ border-bottom: 0 !important;
+
+ .row {
+ margin-top: 16px;
+ }
.previous {
- padding-right: 20px;
+ visibility: visible;
}
.next {
- padding-left: 20px;
+ visibility: visible;
}
}
th:first-child {
text-align:left;
+ padding-left: 12px;
}
th {
cursor: pointer;
- border-left: 0px;
text-align: right;
- border-bottom: 0px;
+ padding-right: 0 !important;
&#evolution {
text-align: center;
@@ -109,23 +100,25 @@
}
.site_search input {
- margin-right: 0px;
+ margin-right: 0;
margin-left: 25px;
padding-right: 25px;
+ width: 250px;
+ height: 3rem;
}
.search_ico {
position: relative;
- left: -25px;
- margin-right: 0px;
- margin-top: -1px;
+ left: -20px;
+ top: 1px;
cursor: pointer;
+ font-size: 16px;
}
.reset {
position: relative;
left: -25px;
cursor: pointer;
- margin-right: 0px;
+ margin-right: 0;
}
tr.group {
@@ -178,13 +171,6 @@
padding:20px
}
- .allWebsitesLoadingIndicator {
- background: url(plugins/Morpheus/images/loading-blue.gif) no-repeat right 3px;
- display: inline-block;
- width: 16px;
- height: 16px;
- }
-
.heading {
display: inline;
margin-top: 4px;
@@ -192,6 +178,9 @@
#evolution_selector {
margin: -6px 0 0 5px;
+ height: 20px;
+ width: 70px;
+ display: inline-block;
}
.label .arrow {
@@ -203,7 +192,7 @@
float: none;
display: inline-block;
vertical-align: top;
- margin: 0px;
+ margin: 0;
margin-left: 6px;
margin-top: -1px;
}
@@ -212,7 +201,7 @@
.multisites_asc,
.multisites_desc {
margin-right: 6px;
- margin-left: 0px;
+ margin-left: 0;
}
.evolution {
vertical-align: top;
@@ -246,18 +235,6 @@
}
tfoot td {
- border-bottom: 0px;
+ border-bottom: 0;
}
}
-
-#mt thead {
- line-height: 2.5em;
-}
-
-#mt thead *:first-child {
- border-top-left-radius: 7px
-}
-
-#mt thead *:last-child {
- border-top-right-radius: 7px;
-}
diff --git a/plugins/MultiSites/templates/getSitesInfo.twig b/plugins/MultiSites/templates/getSitesInfo.twig
index 94d13ee702..ff3ca882bf 100644
--- a/plugins/MultiSites/templates/getSitesInfo.twig
+++ b/plugins/MultiSites/templates/getSitesInfo.twig
@@ -10,14 +10,24 @@
{% block content %}
<div class="container" id="multisites">
- <div id="main">
- <div piwik-multisites-dashboard
- display-revenue-column="{% if displayRevenueColumn %}true{% else %}false{%endif%}"
- page-size="{{ limit }}"
- show-sparklines="{% if show_sparklines %}true{% else %}false{%endif%}"
- date-sparkline="{{ dateSparkline }}"
- auto-refresh-today-report="{{ autoRefreshTodayReport }}">
+ {% if isWidgetized %}
+ <div id="main">
+ {% else %}
+ <div id="main" class="card">
+ <div class="card-content">
+ {% endif %}
+ <div piwik-multisites-dashboard
+ display-revenue-column="{% if displayRevenueColumn %}true{% else %}false{%endif%}"
+ page-size="{{ limit }}"
+ show-sparklines="{% if show_sparklines %}true{% else %}false{%endif%}"
+ date-sparkline="{{ dateSparkline }}"
+ auto-refresh-today-report="{{ autoRefreshTodayReport }}">
+ </div>
+ {% if isWidgetized %}
</div>
+ {% else %}
+ </div></div>
+ {% endif %}
</div>
</div>
{% endblock %}
diff --git a/plugins/Overlay/javascripts/Piwik_Overlay.js b/plugins/Overlay/javascripts/Piwik_Overlay.js
index 35584be177..8183950542 100644
--- a/plugins/Overlay/javascripts/Piwik_Overlay.js
+++ b/plugins/Overlay/javascripts/Piwik_Overlay.js
@@ -230,7 +230,9 @@ var Piwik_Overlay = (function () {
}
});
- if (!optionMatchFound) {
+ if (optionMatchFound) {
+ $select.material_select();
+ } else {
$select.prepend('<option selected="selected">');
}
diff --git a/plugins/Overlay/stylesheets/overlay.css b/plugins/Overlay/stylesheets/overlay.css
index a13dd67a46..465bf7fb76 100644
--- a/plugins/Overlay/stylesheets/overlay.css
+++ b/plugins/Overlay/stylesheets/overlay.css
@@ -15,10 +15,6 @@ body #header {
margin-left: -6px;
}
-body #logo {
- margin-top: 5px;
-}
-
a#overlayTitle {
font-size: 12px;
text-decoration: none;
@@ -124,7 +120,7 @@ a#overlayTransitions:hover {
}
.overlayMetricValue {
- font-size: 14px;
+ font-size: 12px;
font-weight: bold;
}
diff --git a/plugins/PrivacyManager/API.php b/plugins/PrivacyManager/API.php
new file mode 100644
index 0000000000..127523c53d
--- /dev/null
+++ b/plugins/PrivacyManager/API.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\PrivacyManager;
+
+use Piwik\DataTable;
+use Piwik\DataTable\Row;
+use Piwik\Piwik;
+use Piwik\Config as PiwikConfig;
+
+/**
+ * API for plugin PrivacyManager
+ *
+ * @method static \Piwik\Plugins\PrivacyManager\API getInstance()
+ */
+class API extends \Piwik\Plugin\API
+{
+
+ /**
+ * @internal
+ */
+ public function setAnonymizeIpSettings($anonymizeIPEnable, $maskLength, $useAnonymizedIpForVisitEnrichment)
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ if ($anonymizeIPEnable == '1') {
+ IPAnonymizer::activate();
+ } else if ($anonymizeIPEnable == '0') {
+ IPAnonymizer::deactivate();
+ } else {
+ // pass
+ }
+
+ $privacyConfig = new Config();
+ $privacyConfig->ipAddressMaskLength = (int) $maskLength;
+ $privacyConfig->useAnonymizedIpForVisitEnrichment = (bool) $useAnonymizedIpForVisitEnrichment;
+
+ return true;
+ }
+
+ /**
+ * @internal
+ */
+ public function deactivateDoNotTrack()
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ $dntChecker = new DoNotTrackHeaderChecker();
+ $dntChecker->deactivate();
+
+ return true;
+ }
+
+ /**
+ * @internal
+ */
+ public function activateDoNotTrack()
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ $dntChecker = new DoNotTrackHeaderChecker();
+ $dntChecker->activate();
+
+ return true;
+ }
+
+ /**
+ * @internal
+ */
+ public function setScheduleReportDeletionSettings($deleteLowestInterval = 7)
+ {
+ return $this->savePurgeDataSettings(array(
+ 'delete_logs_schedule_lowest_interval' => (int) $deleteLowestInterval
+ ));
+ }
+
+ /**
+ * @internal
+ */
+ public function setDeleteLogsSettings($enableDeleteLogs = '0', $deleteLogsOlderThan = 180)
+ {
+ $deleteLogsOlderThan = (int) $deleteLogsOlderThan;
+ if ($deleteLogsOlderThan < 1) {
+ $deleteLogsOlderThan = 1;
+ }
+
+ return $this->savePurgeDataSettings(array(
+ 'delete_logs_enable' => !empty($enableDeleteLogs),
+ 'delete_logs_older_than' => $deleteLogsOlderThan,
+ ));
+ }
+
+ /**
+ * @internal
+ */
+ public function setDeleteReportsSettings($enableDeleteReports = 0, $deleteReportsOlderThan = 3,
+ $keepBasic = 0, $keepDay = 0, $keepWeek = 0, $keepMonth = 0,
+ $keepYear = 0, $keepRange = 0, $keepSegments = 0)
+ {
+ $settings = array();
+
+ // delete reports settings
+ $settings['delete_reports_enable'] = !empty($enableDeleteReports);
+
+ $deleteReportsOlderThan = (int) $deleteReportsOlderThan;
+ if ($deleteReportsOlderThan < 3) {
+ $deleteReportsOlderThan = 3;
+ }
+
+ $settings['delete_reports_older_than'] = $deleteReportsOlderThan;
+
+ $settings['delete_reports_keep_basic_metrics'] = (int) $keepBasic;
+ $settings['delete_reports_keep_day_reports'] = (int) $keepDay;
+ $settings['delete_reports_keep_week_reports'] = (int) $keepWeek;
+ $settings['delete_reports_keep_month_reports'] = (int) $keepMonth;
+ $settings['delete_reports_keep_year_reports'] = (int) $keepYear;
+ $settings['delete_reports_keep_range_reports'] = (int) $keepRange;
+ $settings['delete_reports_keep_segment_reports'] = (int) $keepSegments;
+ $settings['delete_logs_max_rows_per_query'] = PiwikConfig::getInstance()->Deletelogs['delete_logs_max_rows_per_query'];
+
+ return $this->savePurgeDataSettings($settings);
+ }
+
+ private function savePurgeDataSettings($settings)
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ $this->checkDataPurgeAdminSettingsIsEnabled();
+
+ PrivacyManager::savePurgeDataSettings($settings);
+
+ return true;
+ }
+
+ private function checkDataPurgeAdminSettingsIsEnabled()
+ {
+ if (!Controller::isDataPurgeSettingsEnabled()) {
+ throw new \Exception("Configuring deleting log data and report data has been disabled by Piwik admins.");
+ }
+ }
+}
diff --git a/plugins/PrivacyManager/Controller.php b/plugins/PrivacyManager/Controller.php
index c385f84376..1e9a67b487 100644
--- a/plugins/PrivacyManager/Controller.php
+++ b/plugins/PrivacyManager/Controller.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\PrivacyManager;
use Piwik\Common;
use Piwik\Config as PiwikConfig;
use Piwik\Container\StaticContainer;
+use Piwik\DataTable\Renderer\Json;
use Piwik\Date;
use Piwik\Db;
use Piwik\Metrics\Formatter;
@@ -31,45 +32,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
const ACTIVATE_DNT_NONCE = 'PrivacyManager.activateDnt';
const DEACTIVATE_DNT_NONCE = 'PrivacyManager.deactivateDnt';
- public function saveSettings()
- {
- Piwik::checkUserHasSuperUserAccess();
- if ($_SERVER["REQUEST_METHOD"] == "POST") {
- $this->checkTokenInUrl();
- switch (Common::getRequestVar('form')) {
- case("formMaskLength"):
- $enable = Common::getRequestVar("anonymizeIPEnable", 0);
- if ($enable == 1) {
- IPAnonymizer::activate();
- } else if ($enable == 0) {
- IPAnonymizer::deactivate();
- } else {
- // pass
- }
-
- $privacyConfig = new Config();
- $privacyConfig->ipAddressMaskLength = Common::getRequestVar("maskLength", 1);
- $privacyConfig->useAnonymizedIpForVisitEnrichment = Common::getRequestVar("useAnonymizedIpForVisitEnrichment", 1);
- break;
-
- case("formDeleteSettings"):
- $this->checkDataPurgeAdminSettingsIsEnabled();
- $settings = $this->getPurgeSettingsFromRequest();
- PrivacyManager::savePurgeDataSettings($settings);
- break;
-
- default: //do nothing
- break;
- }
- }
-
- $notification = new Notification(Piwik::translate('General_YourChangesHaveBeenSaved'));
- $notification->context = Notification::CONTEXT_SUCCESS;
- Notification\Manager::notify('PrivacyManager_ChangesHaveBeenSaved', $notification);
-
- $this->redirectToIndex('PrivacyManager', 'privacySettings', null, null, null, array('updated' => 1));
- }
-
private function checkDataPurgeAdminSettingsIsEnabled()
{
if (!self::isDataPurgeSettingsEnabled()) {
@@ -88,22 +50,22 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$settings = array();
// delete logs settings
- $settings['delete_logs_enable'] = Common::getRequestVar("deleteEnable", 0);
+ $settings['delete_logs_enable'] = Common::getRequestVar("enableDeleteLogs", 0);
$settings['delete_logs_schedule_lowest_interval'] = Common::getRequestVar("deleteLowestInterval", 7);
- $settings['delete_logs_older_than'] = ((int)Common::getRequestVar("deleteOlderThan", 180) < 1) ?
+ $settings['delete_logs_older_than'] = ((int)Common::getRequestVar("deleteLogsOlderThan", 180) < 1) ?
1 : Common::getRequestVar("deleteOlderThan", 180);
// delete reports settings
- $settings['delete_reports_enable'] = Common::getRequestVar("deleteReportsEnable", 0);
+ $settings['delete_reports_enable'] = Common::getRequestVar("enableDeleteReports", 0);
$deleteReportsOlderThan = Common::getRequestVar("deleteReportsOlderThan", 3);
$settings['delete_reports_older_than'] = $deleteReportsOlderThan < 3 ? 3 : $deleteReportsOlderThan;
- $settings['delete_reports_keep_basic_metrics'] = Common::getRequestVar("deleteReportsKeepBasic", 0);
- $settings['delete_reports_keep_day_reports'] = Common::getRequestVar("deleteReportsKeepDay", 0);
- $settings['delete_reports_keep_week_reports'] = Common::getRequestVar("deleteReportsKeepWeek", 0);
- $settings['delete_reports_keep_month_reports'] = Common::getRequestVar("deleteReportsKeepMonth", 0);
- $settings['delete_reports_keep_year_reports'] = Common::getRequestVar("deleteReportsKeepYear", 0);
- $settings['delete_reports_keep_range_reports'] = Common::getRequestVar("deleteReportsKeepRange", 0);
- $settings['delete_reports_keep_segment_reports'] = Common::getRequestVar("deleteReportsKeepSegments", 0);
+ $settings['delete_reports_keep_basic_metrics'] = Common::getRequestVar("keepBasic", 0);
+ $settings['delete_reports_keep_day_reports'] = Common::getRequestVar("keepDay", 0);
+ $settings['delete_reports_keep_week_reports'] = Common::getRequestVar("keepWeek", 0);
+ $settings['delete_reports_keep_month_reports'] = Common::getRequestVar("keepMonth", 0);
+ $settings['delete_reports_keep_year_reports'] = Common::getRequestVar("keepYear", 0);
+ $settings['delete_reports_keep_range_reports'] = Common::getRequestVar("keepRange", 0);
+ $settings['delete_reports_keep_segment_reports'] = Common::getRequestVar("keepSegments", 0);
$settings['delete_logs_max_rows_per_query'] = PiwikConfig::getInstance()->Deletelogs['delete_logs_max_rows_per_query'];
return $settings;
@@ -139,6 +101,44 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$view->dbUser = PiwikConfig::getInstance()->database['username'];
$view->deactivateNonce = Nonce::getNonce(self::DEACTIVATE_DNT_NONCE);
$view->activateNonce = Nonce::getNonce(self::ACTIVATE_DNT_NONCE);
+
+ $view->maskLengthOptions = array(
+ array('key' => '1',
+ 'value' => Piwik::translate('PrivacyManager_AnonymizeIpMaskLength', array("1","192.168.100.xxx")),
+ 'description' => ''),
+ array('key' => '2',
+ 'value' => Piwik::translate('PrivacyManager_AnonymizeIpMaskLength', array("2","192.168.xxx.xxx")),
+ 'description' => Piwik::translate('General_Recommended')),
+ array('key' => '3',
+ 'value' => Piwik::translate('PrivacyManager_AnonymizeIpMaskLength', array("3","192.xxx.xxx.xxx")),
+ 'description' => '')
+ );
+ $view->useAnonymizedIpForVisitEnrichmentOptions = array(
+ array('key' => '1',
+ 'value' => Piwik::translate('General_Yes'),
+ 'description' => Piwik::translate('PrivacyManager_RecommendedForPrivacy')),
+ array(
+ 'key' => '0',
+ 'value' => Piwik::translate('General_No'),
+ 'description' => ''
+ )
+ );
+ $view->scheduleDeletionOptions = array(
+ array('key' => '1',
+ 'value' => Piwik::translate('Intl_PeriodDay')),
+ array('key' => '7',
+ 'value' => Piwik::translate('Intl_PeriodWeek')),
+ array('key' => '30',
+ 'value' => Piwik::translate('Intl_PeriodMonth'))
+ );
+ $view->doNotTrackOptions = array(
+ array('key' => '1',
+ 'value' => Piwik::translate('PrivacyManager_DoNotTrack_Enable'),
+ 'description' => Piwik::translate('General_Recommended')),
+ array('key' => '0',
+ 'value' => Piwik::translate('PrivacyManager_DoNotTrack_Disable'),
+ 'description' => Piwik::translate('General_NotRecommended'))
+ );
}
$view->language = LanguagesManager::getLanguageCodeForCurrentUser();
$this->setBasicVariablesView($view);
@@ -240,9 +240,12 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$anonymizeIP = array();
$privacyConfig = new Config();
- $anonymizeIP["enabled"] = IpAnonymizer::isActive();
+ $anonymizeIP["enabled"] = IPAnonymizer::isActive();
$anonymizeIP["maskLength"] = $privacyConfig->ipAddressMaskLength;
$anonymizeIP["useAnonymizedIpForVisitEnrichment"] = $privacyConfig->useAnonymizedIpForVisitEnrichment;
+ if (!$anonymizeIP["useAnonymizedIpForVisitEnrichment"]) {
+ $anonymizeIP["useAnonymizedIpForVisitEnrichment"] = '0';
+ }
return $anonymizeIP;
}
@@ -299,25 +302,4 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
return $deleteDataInfos;
}
- public function deactivateDoNotTrack()
- {
- Piwik::checkUserHasSuperUserAccess();
- Nonce::checkNonce(self::DEACTIVATE_DNT_NONCE);
-
- $dntChecker = new DoNotTrackHeaderChecker();
- $dntChecker->deactivate();
-
- $this->redirectToIndex('PrivacyManager', 'privacySettings');
- }
-
- public function activateDoNotTrack()
- {
- Piwik::checkUserHasSuperUserAccess();
- Nonce::checkNonce(self::ACTIVATE_DNT_NONCE);
-
- $dntChecker = new DoNotTrackHeaderChecker();
- $dntChecker->activate();
-
- $this->redirectToIndex('PrivacyManager', 'privacySettings');
- }
}
diff --git a/plugins/PrivacyManager/Menu.php b/plugins/PrivacyManager/Menu.php
index f84bdf66dc..9c77c5b0d5 100644
--- a/plugins/PrivacyManager/Menu.php
+++ b/plugins/PrivacyManager/Menu.php
@@ -16,7 +16,7 @@ class Menu extends \Piwik\Plugin\Menu
public function configureAdminMenu(MenuAdmin $menu)
{
if (Piwik::isUserHasSomeAdminAccess()) {
- $menu->addManageItem('PrivacyManager_MenuPrivacySettings',
+ $menu->addSystemItem('PrivacyManager_MenuPrivacySettings',
$this->urlForAction('privacySettings'),
$order = 25);
}
diff --git a/plugins/PrivacyManager/PrivacyManager.php b/plugins/PrivacyManager/PrivacyManager.php
index 5c4ec03ad2..b5f9066d7b 100644
--- a/plugins/PrivacyManager/PrivacyManager.php
+++ b/plugins/PrivacyManager/PrivacyManager.php
@@ -141,9 +141,15 @@ class PrivacyManager extends Plugin
'Tracker.setVisitorIp' => array($this->ipAnonymizer, 'setVisitorIpAddress'),
'Installation.defaultSettingsForm.init' => 'installationFormInit',
'Installation.defaultSettingsForm.submit' => 'installationFormSubmit',
+ 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
);
}
+ public function getClientSideTranslationKeys(&$translationKeys)
+ {
+ $translationKeys[] = 'CoreAdminHome_SettingsSaveSuccess';
+ }
+
public function setTrackerCacheGeneral(&$cacheContent)
{
$config = new Config();
@@ -152,7 +158,12 @@ class PrivacyManager extends Plugin
public function getJsFiles(&$jsFiles)
{
- $jsFiles[] = "plugins/PrivacyManager/javascripts/privacySettings.js";
+ $jsFiles[] = "plugins/PrivacyManager/angularjs/report-deletion.model.js";
+ $jsFiles[] = "plugins/PrivacyManager/angularjs/schedule-report-deletion/schedule-report-deletion.controller.js";
+ $jsFiles[] = "plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js";
+ $jsFiles[] = "plugins/PrivacyManager/angularjs/do-not-track-preference/do-not-track-preference.controller.js";
+ $jsFiles[] = "plugins/PrivacyManager/angularjs/delete-old-logs/delete-old-logs.controller.js";
+ $jsFiles[] = "plugins/PrivacyManager/angularjs/delete-old-reports/delete-old-reports.controller.js";
}
/**
diff --git a/plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js b/plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js
new file mode 100644
index 0000000000..5e6931f4f1
--- /dev/null
+++ b/plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js
@@ -0,0 +1,38 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('AnonymizeIpController', AnonymizeIpController);
+
+ AnonymizeIpController.$inject = ['piwikApi'];
+
+ function AnonymizeIpController(piwikApi) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+ this.isLoading = false;
+
+ this.save = function () {
+ this.isLoading = true;
+
+ piwikApi.post({module: 'API', method: 'PrivacyManager.setAnonymizeIpSettings'}, {
+ anonymizeIPEnable: this.enabled ? '1' : '0',
+ maskLength: this.maskLength,
+ useAnonymizedIpForVisitEnrichment: parseInt(this.useAnonymizedIpForVisitEnrichment, 10) ? '1' : '0'
+ }).then(function (success) {
+ self.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {context: 'success', id:'privacyManagerSettings'});
+ notification.scrollToNotification();
+
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/PrivacyManager/angularjs/delete-old-logs/delete-old-logs.controller.js b/plugins/PrivacyManager/angularjs/delete-old-logs/delete-old-logs.controller.js
new file mode 100644
index 0000000000..29798bb9a0
--- /dev/null
+++ b/plugins/PrivacyManager/angularjs/delete-old-logs/delete-old-logs.controller.js
@@ -0,0 +1,54 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('DeleteOldLogsController', DeleteOldLogsController);
+
+ DeleteOldLogsController.$inject = ['reportDeletionModel', 'piwikApi', '$timeout'];
+
+ function DeleteOldLogsController(reportDeletionModel, piwikApi, $timeout) {
+
+ var self = this;
+
+ this.isLoading = false;
+
+ function saveSettings()
+ {
+ var method = 'PrivacyManager.setDeleteLogsSettings';
+ reportDeletionModel.savePurageDataSettings(self, method, self.getSettings());
+ }
+
+ this.getSettings = function () {
+ return {
+ enableDeleteLogs: this.enabled ? '1' : '0',
+ deleteLogsOlderThan: this.deleteOlderThan
+ };
+ }
+
+ this.reloadDbStats = function () {
+ reportDeletionModel.updateSettings(this.getSettings());
+ }
+
+ $timeout(function () {
+ reportDeletionModel.initSettings(self.getSettings());
+ });
+
+ this.save = function () {
+
+ if (this.enabled) {
+ var confirmId = 'deleteLogsConfirm';
+ if (reportDeletionModel.settings && '1' === reportDeletionModel.settings.enableDeleteReports) {
+ confirmId = 'deleteBothConfirm';
+ }
+ $('#confirmDeleteSettings').find('>h2').hide();
+ $("#" + confirmId).show();
+ piwikHelper.modalConfirm('#confirmDeleteSettings', {yes: saveSettings});
+ } else {
+ saveSettings();
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/PrivacyManager/angularjs/delete-old-reports/delete-old-reports.controller.js b/plugins/PrivacyManager/angularjs/delete-old-reports/delete-old-reports.controller.js
new file mode 100644
index 0000000000..5f637dc740
--- /dev/null
+++ b/plugins/PrivacyManager/angularjs/delete-old-reports/delete-old-reports.controller.js
@@ -0,0 +1,66 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('DeleteOldReportsController', DeleteOldReportsController);
+
+ DeleteOldReportsController.$inject = ['reportDeletionModel', 'piwikApi', '$timeout'];
+
+ function DeleteOldReportsController(reportDeletionModel, piwikApi, $timeout) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+ this.isLoading = false;
+
+ function getInt(value)
+ {
+ return value ? '1' : '0';
+ }
+
+ function saveSettings()
+ {
+ var method = 'PrivacyManager.setDeleteReportsSettings';
+ reportDeletionModel.savePurageDataSettings(self, method, self.getSettings());
+ }
+
+ this.getSettings = function () {
+ return {
+ enableDeleteReports: this.enabled ? '1' : '0',
+ deleteReportsOlderThan: this.deleteOlderThan,
+ keepBasic: getInt(this.keepBasic),
+ keepDay: getInt(this.keepDataForDay),
+ keepWeek: getInt(this.keepDataForWeek),
+ keepMonth: getInt(this.keepDataForMonth),
+ keepYear: getInt(this.keepDataForYear),
+ keepRange: getInt(this.keepDataForRange),
+ keepSegments: getInt(this.keepDataForSegments),
+ };
+ }
+
+ this.reloadDbStats = function () {
+ reportDeletionModel.updateSettings(this.getSettings());
+ }
+
+ $timeout(function () {
+ reportDeletionModel.initSettings(self.getSettings());
+ });
+
+ this.save = function () {
+
+ if (this.enabled) {
+ var confirmId = 'deleteReportsConfirm';
+ if (reportDeletionModel.settings && '1' === reportDeletionModel.settings.enableDeleteLogs) {
+ confirmId = 'deleteBothConfirm';
+ }
+ $('#confirmDeleteSettings').find('>h2').hide();
+ $("#" + confirmId).show();
+ piwikHelper.modalConfirm('#confirmDeleteSettings', {yes: saveSettings});
+ } else {
+ saveSettings();
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/PrivacyManager/angularjs/do-not-track-preference/do-not-track-preference.controller.js b/plugins/PrivacyManager/angularjs/do-not-track-preference/do-not-track-preference.controller.js
new file mode 100644
index 0000000000..fd80ca2609
--- /dev/null
+++ b/plugins/PrivacyManager/angularjs/do-not-track-preference/do-not-track-preference.controller.js
@@ -0,0 +1,40 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('DoNotTrackPreferenceController', DoNotTrackPreferenceController);
+
+ DoNotTrackPreferenceController.$inject = ['piwikApi'];
+
+ function DoNotTrackPreferenceController(piwikApi) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+ this.isLoading = false;
+
+ this.save = function () {
+ this.isLoading = true;
+
+ var action = 'deactivateDoNotTrack';
+ if (this.enabled === '1') {
+ action = 'activateDoNotTrack';
+ }
+
+ piwikApi.post({module: 'API', method: 'PrivacyManager.' + action}).then(function (success) {
+
+ self.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {context: 'success', id:'privacyManagerSettings'});
+ notification.scrollToNotification();
+
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/PrivacyManager/angularjs/report-deletion.model.js b/plugins/PrivacyManager/angularjs/report-deletion.model.js
new file mode 100644
index 0000000000..5d2bd5799c
--- /dev/null
+++ b/plugins/PrivacyManager/angularjs/report-deletion.model.js
@@ -0,0 +1,111 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp.service').factory('reportDeletionModel', reportDeletionModel);
+
+ reportDeletionModel.$inject = ['piwik', 'piwikApi'];
+
+ function reportDeletionModel (piwik, piwikApi) {
+
+ var currentRequest;
+ var isFirstLoad = true;
+
+ var model = {
+ settings: {},
+ showEstimate: false,
+ loadingEstimation: false,
+ estimation: '',
+ isModified: false,
+ isEitherDeleteSectionEnabled: isEitherDeleteSectionEnabled,
+ reloadDbStats: reloadDbStats,
+ savePurageDataSettings: savePurageDataSettings,
+ updateSettings: updateSettings,
+ initSettings: initSettings
+ };
+
+ return model;
+
+ function updateSettings(settings)
+ {
+ initSettings(settings);
+ model.isModified = true;
+ }
+
+ function initSettings(settings)
+ {
+ model.settings = angular.merge({}, model.settings, settings);
+ model.reloadDbStats();
+ }
+
+ function savePurageDataSettings(controller, apiMethod, settings)
+ {
+ controller.isLoading = true;
+ model.isModified = false;
+
+ return piwikApi.post({
+ module: 'API', method: apiMethod
+ }, settings).then(function () {
+ controller.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {context: 'success', id:'privacyManagerSettings'});
+ notification.scrollToNotification();
+ }, function () {
+ controller.isLoading = false;
+ });
+ }
+
+ function isEitherDeleteSectionEnabled() {
+ return ('1' === model.settings.enableDeleteLogs || '1' === model.settings.enableDeleteReports);
+ }
+
+ function isManualEstimationLinkShowing()
+ {
+ return $('#getPurgeEstimateLink').length > 0;
+ }
+
+ /**
+ * @param {boolean} [forceEstimate] (defaults to false)
+ */
+ function reloadDbStats(forceEstimate) {
+ if (currentRequest) {
+ currentRequest.abort();
+ }
+
+ // if the manual estimate link is showing, abort unless forcing
+ if (forceEstimate !== true
+ && (!isEitherDeleteSectionEnabled() || isManualEstimationLinkShowing())) {
+ return;
+ }
+
+ model.loadingEstimation = true;
+ model.estimation = '';
+ model.showEstimate = false;
+
+ var formData = model.settings;
+
+ if (forceEstimate === true) {
+ formData['forceEstimate'] = 1;
+ }
+
+ currentRequest = piwikApi.post({
+ module: 'PrivacyManager',
+ action: 'getDatabaseSize',
+ format: 'html'
+ }, formData).then(function (data) {
+ currentRequest = undefined;
+ model.estimation = data;
+ model.showEstimate = true;
+ model.loadingEstimation = false;
+ }, function () {
+ model.loadingEstimation = true;
+ });
+ }
+
+ }
+})(); \ No newline at end of file
diff --git a/plugins/PrivacyManager/angularjs/schedule-report-deletion/schedule-report-deletion.controller.js b/plugins/PrivacyManager/angularjs/schedule-report-deletion/schedule-report-deletion.controller.js
new file mode 100644
index 0000000000..33ebabd1d0
--- /dev/null
+++ b/plugins/PrivacyManager/angularjs/schedule-report-deletion/schedule-report-deletion.controller.js
@@ -0,0 +1,65 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ScheduleReportDeletionController', ScheduleReportDeletionController);
+
+ ScheduleReportDeletionController.$inject = ['reportDeletionModel', 'piwikApi', '$timeout'];
+
+ function ScheduleReportDeletionController(reportDeletionModel, piwikApi, $timeout) {
+
+ var self = this;
+ this.isLoading = false;
+ this.dataWasPurged = false;
+ this.showPurgeNowLink = true;
+ this.model = reportDeletionModel;
+
+ this.save = function () {
+ var method = 'PrivacyManager.setScheduleReportDeletionSettings';
+ reportDeletionModel.model.savePurageDataSettings(this, method, {
+ deleteLowestInterval: this.deleteLowestInterval
+ });
+ };
+
+ this.executeDataPurgeNow = function () {
+
+ if (reportDeletionModel.isModified) {
+ piwikHelper.modalConfirm('#saveSettingsBeforePurge', {yes: function () {}});
+ return;
+ }
+
+ // ask user if they really want to delete their old data
+ piwikHelper.modalConfirm('#confirmPurgeNow', {
+ yes: function () {
+ self.loadingDataPurge = true;
+ self.showPurgeNowLink = false;
+
+ // execute a data purge
+ piwikApi.withTokenInUrl();
+ var ajaxRequest = piwikApi.fetch({
+ module: 'PrivacyManager',
+ action: 'executeDataPurge',
+ format: 'html'
+ }).then(function () {
+ self.loadingDataPurge = false;
+ // force reload
+ reportDeletionModel.reloadDbStats();
+
+ self.dataWasPurged = true;
+
+ $timeout(function () {
+ self.dataWasPurged = false;
+ self.showPurgeNowLink = true;
+ }, 2000);
+ }, function () {
+ self.loadingDataPurge = false;
+ });
+ }
+ });
+ };
+
+ }
+})(); \ No newline at end of file
diff --git a/plugins/PrivacyManager/javascripts/privacySettings.js b/plugins/PrivacyManager/javascripts/privacySettings.js
deleted file mode 100644
index b50ed40563..0000000000
--- a/plugins/PrivacyManager/javascripts/privacySettings.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-$(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('scheduleSettingsHeadline', showSection);
- toggleBlock('databaseSizeHeadline', showSection);
- toggleBlock('deleteSchedulingSettings', showSection);
- }
-
- // reloads purged database size estimate
- var currentRequest;
-
- /**
- * @param {boolean} [forceEstimate] (defaults to false)
- */
- 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++) {
- formData[data[i].name] = data[i].value;
- }
- if (forceEstimate === true) {
- formData['forceEstimate'] = 1;
- }
-
- currentRequest = new ajaxHelper();
- currentRequest.setLoadingElement('#deleteDataEstimateSect .loadingPiwik');
- currentRequest.addParams({
- module: 'PrivacyManager',
- action: 'getDatabaseSize'
- }, 'get');
- currentRequest.addParams(formData, 'post');
- currentRequest.setCallback(
- function (data) {
- currentRequest = undefined;
- $('#deleteDataEstimate').html(data).show();
-
- // lock size of db size estimate
- $('#deleteDataEstimateSect').height($('#deleteDataEstimateSect').height());
- }
- );
- currentRequest.setFormat('html');
- currentRequest.send(false);
- }
-
- // make sure certain sections only display if their corresponding features are enabled
- $('input[name=anonymizeIPEnable]').change(function () {
- toggleBlock("anonymizeIPenabled", $(this).val());
- });
-
- $('input[name=deleteEnable]').change(function () {
- toggleBlock("deleteLogSettings", $(this).val());
- toggleOtherDeleteSections();
- }).change(reloadDbStats);
-
- $('input[name=deleteReportsEnable]').change(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());
- // This one is in an AngularJS directive, so is generated later
- setTimeout(function () {
- toggleBlock("deleteOldReportsMoreInfo", $("input[name=deleteReportsEnable]:checked").val());
- }, 500);
- toggleOtherDeleteSections();
- });
-
- // make sure the DB size estimate is reloaded every time a delete logs/reports setting is changed
- $('#formDeleteSettings').find('input[type=text]').each(function () {
- $(this).change(reloadDbStats);
- });
- $('#formDeleteSettings').find('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').find('>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').find('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({
- module: 'PrivacyManager',
- action: 'executeDataPurge'
- }, 'get');
- ajaxRequest.withTokenInUrl();
- ajaxRequest.setCallback(
- function () {
- // force reload
- $('#deleteDataEstimate').html('');
- reloadDbStats();
-
- // show 'db purged' message
- $('#db-purged-message').fadeIn('slow');
- setTimeout(function () {
- // hide 'db purged' message & show link
- $('#db-purged-message').fadeOut('slow', function () {
- $(link).show();
- });
- }, 2000);
- }
- );
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
- }
- });
- });
-
- // 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.twig b/plugins/PrivacyManager/templates/privacySettings.twig
index 98f6f58efb..ab26aa3bef 100644
--- a/plugins/PrivacyManager/templates/privacySettings.twig
+++ b/plugins/PrivacyManager/templates/privacySettings.twig
@@ -5,78 +5,51 @@
{% block content %}
{% import 'macros.twig' as piwik %}
{% if isSuperUser %}
- <h2 piwik-enriched-headline
- help-url="http://piwik.org/docs/privacy/">{{ title }}</h2>
+
+<div piwik-content-intro>
+ <h2 piwik-enriched-headline help-url="http://piwik.org/docs/privacy/">{{ title }}</h2>
+
<p>{{ 'PrivacyManager_Teaser'|translate('<a href="#anonymizeIPAnchor">',"</a>",'<a href="#deleteLogsAnchor">',"</a>",'<a href="#optOutAnchor">',"</a>")|raw }}
{{'PrivacyManager_SeeAlsoOurOfficialGuidePrivacy'|translate('<a href="http://piwik.org/privacy/" rel="noreferrer" target="_blank">','</a>')|raw }}</p>
+</div>
- <h2 id="anonymizeIPAnchor">{{ 'PrivacyManager_UseAnonymizeIp'|translate }}</h2>
- <form method="post" action="{{ {'action':'saveSettings', 'form':'formMaskLength', 'token_auth':token_auth} | urlRewriteWithParameters }}" id="formMaskLength">
- <div id="anonymizeIpSettings" class="form-group">
- <label>
- {{ 'PrivacyManager_UseAnonymizeIp'|translate }}
- </label>
- <div class="form-help">
- {{ 'PrivacyManager_AnonymizeIpInlineHelp'|translate }}
- {{ 'PrivacyManager_AnonymizeIpDescription'|translate }}
- </div>
- <label class="radio">
- <input id="anonymizeIPEnable-1" type="radio" name="anonymizeIPEnable" value="1" {% if anonymizeIP.enabled == '1' %}checked {% endif %}/>
- {{ 'General_Yes'|translate }}
- </label>
- <label class="radio">
- <input class="indented-radio-button" id="anonymizeIPEnable-0" type="radio" name="anonymizeIPEnable" value="0" {% if anonymizeIP.enabled == '0' %} checked {% endif %}/>
- {{ 'General_No'|translate }}
- </label>
- <input type="hidden" name="token_auth" value="{{ token_auth }}"/>
+<div piwik-content-block
+ id="anonymizeIPAnchor"
+ content-title="{{ 'PrivacyManager_UseAnonymizeIp'|translate|e('html_attr') }}">
+ <div piwik-form ng-controller="AnonymizeIpController as anonymizeIp">
+
+ <div piwik-field uicontrol="checkbox" name="anonymizeIpSettings"
+ ng-model="anonymizeIp.enabled"
+ title="{{ 'PrivacyManager_UseAnonymizeIp'|translate|e('html_attr') }}"
+ value="{{ anonymizeIP.enabled }}"
+ inline-help="{{ 'PrivacyManager_AnonymizeIpInlineHelp'|translate|e('html_attr') }} {{ 'PrivacyManager_AnonymizeIpDescription'|translate|e('html_attr') }}">
</div>
- <div id="anonymizeIPenabled">
- <div class="form-group">
- <label>
- {{ 'PrivacyManager_AnonymizeIpMaskLengtDescription'|translate }}<br/>
- </label>
- <div class="form-help">
- {{ 'PrivacyManager_GeolocationAnonymizeIpNote'|translate }}
- </div>
- <label class="radio">
- <input id="maskLength-1" type="radio" name="maskLength" value="1" {% if anonymizeIP.maskLength == '1' %} checked {% endif %}/>
- {{ 'PrivacyManager_AnonymizeIpMaskLength'|translate("1","192.168.100.xxx") }}
- </label>
- <label class="radio">
- <input id="maskLength-2" type="radio" name="maskLength" value="2" {% if anonymizeIP.maskLength == '2' %} checked {% endif %}/>
- {{ 'PrivacyManager_AnonymizeIpMaskLength'|translate("2","192.168.xxx.xxx") }}
- <span class="form-description">{{ 'General_Recommended'|translate }}</span>
- </label>
- <label class="radio">
- <input id="maskLength-3" type="radio" name="maskLength" value="3" {% if anonymizeIP.maskLength == '3' %} checked {% endif %}/>
- {{ 'PrivacyManager_AnonymizeIpMaskLength'|translate("3","192.xxx.xxx.xxx") }}
- </label>
+
+ <div ng-show="anonymizeIp.enabled">
+ <div piwik-field uicontrol="radio" name="maskLength"
+ ng-model="anonymizeIp.maskLength"
+ title="{{ 'PrivacyManager_AnonymizeIpMaskLengtDescription'|translate|e('html_attr') }}"
+ value="{{ anonymizeIP.maskLength }}"
+ options="{{ maskLengthOptions|json_encode }}"
+ inline-help="{{ 'PrivacyManager_GeolocationAnonymizeIpNote'|translate|e('html_attr') }}">
</div>
- <div class="form-group">
- <label>
- {{ 'PrivacyManager_UseAnonymizedIpForVisitEnrichment'|translate }}<br/>
- </label>
- <div class="form-help">
- {{ 'PrivacyManager_UseAnonymizedIpForVisitEnrichmentNote'|translate }}
- </div>
- <label class="radio">
- <input id="useAnonymizedIpForVisitEnrichment-1" type="radio" name="useAnonymizedIpForVisitEnrichment" value="1" {% if anonymizeIP.useAnonymizedIpForVisitEnrichment == '1' %}checked {% endif %}/>
- {{ 'General_Yes'|translate }}
- <span class="form-description">{{ 'PrivacyManager_RecommendedForPrivacy'|translate }}</span>
- </label>
- <label class="radio">
- <input id="useAnonymizedIpForVisitEnrichment-2" type="radio" name="useAnonymizedIpForVisitEnrichment" value="0" {% if anonymizeIP.useAnonymizedIpForVisitEnrichment == '0' %} checked {% endif %}/>
- {{ 'General_No'|translate }}
- </label>
+
+ <div piwik-field uicontrol="radio" name="useAnonymizedIpForVisitEnrichment"
+ ng-model="anonymizeIp.useAnonymizedIpForVisitEnrichment"
+ title="{{ 'PrivacyManager_UseAnonymizedIpForVisitEnrichment'|translate|e('html_attr') }}"
+ value="{% if anonymizeIP.useAnonymizedIpForVisitEnrichment %}1{% else %}0{% endif %}"
+ options="{{ useAnonymizedIpForVisitEnrichmentOptions|json_encode }}"
+ inline-help="{{ 'PrivacyManager_UseAnonymizedIpForVisitEnrichmentNote'|translate|e('html_attr') }}">
</div>
</div>
- <input type="hidden" name="nonce" value="{% if anonymizeIP.enabled %}{{ deactivateNonce }}{% else %}{{ activateNonce }}{% endif %}">
- <input type="submit" value="{{ 'General_Save'|translate }}" id="privacySettingsSubmit"/>
- </form>
-
- {% if isDataPurgeSettingsEnabled %}
+ <div piwik-save-button onconfirm="anonymizeIp.save()" saving="anonymizeIp.isLoading"></div>
+ </div>
+</div>
+{% if isDataPurgeSettingsEnabled %}
+ <div piwik-content-block id="deleteLogsAnchor"
+ content-title="{{ 'PrivacyManager_DeleteOldVisitorLogs'|translate|e('html_attr') }}">
<div class="ui-confirm" id="confirmDeleteSettings">
<h2 id="deleteLogsConfirm">{{ 'PrivacyManager_DeleteLogsConfirm'|translate }}</h2>
@@ -95,32 +68,31 @@
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
- <h2 id="deleteLogsAnchor">{{ 'PrivacyManager_DeleteOldVisitorLogs'|translate }}</h2>
+
<p>{{ 'PrivacyManager_DeleteDataDescription'|translate }} {{ 'PrivacyManager_DeleteDataDescription2'|translate }}</p>
- <form method="post" action="{{ {'action':'saveSettings','form':'formDeleteSettings','token_auth':token_auth} | urlRewriteWithParameters }}" id="formDeleteSettings">
- <div id="deleteLogSettingEnabled" class="form-group">
- <label>
- {{ 'PrivacyManager_UseDeleteLog'|translate }}
- </label>
- <div class="form-help">
+
+ <div piwik-form
+ ng-controller="DeleteOldLogsController as deleteOldLogs"
+ id="formDeleteSettings">
+ <div id="deleteLogSettingEnabled">
+
+ <div id="deleteLogInfoInlineHelp" class="inline-help-node">
{{ 'PrivacyManager_DeleteLogInfo'|translate(deleteData.deleteTables)|raw }}
{% if not canDeleteLogActions %}
- <br/>
- <br/>
+ <br/><br/>
{{ 'PrivacyManager_CannotLockSoDeleteLogActions'|translate(dbUser) }}
{% endif %}
</div>
- <label class="radio">
- <input id="deleteEnable-1" type="radio" name="deleteEnable" value="1" {% if deleteData.config.delete_logs_enable == '1' %} checked {% endif %}/>
- {{ 'General_Yes'|translate }}
- </label>
- <label class="radio">
- <input class="indented-radio-button" id="deleteEnable-2" type="radio" name="deleteEnable" value="0"
- {% if deleteData.config.delete_logs_enable == '0' %} checked {% endif %}/>
- {{ 'General_No'|translate }}
- </label>
- <div class="clearfix"><br/></div>
- <div class="alert alert-warning" style="width: 40%;">
+
+ <div piwik-field uicontrol="checkbox" name="deleteEnable"
+ ng-model="deleteOldLogs.enabled"
+ ng-change="deleteOldLogs.reloadDbStats()"
+ title="{{ 'PrivacyManager_UseDeleteLog'|translate|e('html_attr') }}"
+ value="{{ deleteData.config.delete_logs_enable }}"
+ inline-help="#deleteLogInfoInlineHelp">
+ </div>
+
+ <div class="alert alert-warning" style="width: 50%;">
{{ 'PrivacyManager_DeleteLogDescription2'|translate|raw }}
<a href="http://piwik.org/faq/general/#faq_125" rel="noreferrer" target="_blank">
{{ 'General_ClickHere'|translate }}
@@ -128,112 +100,125 @@
</div>
</div>
- <div id="deleteLogSettings" class="form-group">
- <label for="deleteOlderThan">
- {{ 'PrivacyManager_DeleteLogsOlderThan'|translate }}
- </label>
- <div class="form-help">
- {{ 'PrivacyManager_LeastDaysInput'|translate("1") }}
- </div>
- <div class="input-group">
- <input type="text" id="deleteOlderThan" value="{{ deleteData.config.delete_logs_older_than }}" name="deleteOlderThan"/>
- <span class="input-group-addon">{{ 'Intl_PeriodDays'|translate }}</span>
+ <div id="deleteLogSettings" ng-show="deleteOldLogs.enabled">
+ <div piwik-field uicontrol="text" name="deleteOlderThan"
+ ng-model="deleteOldLogs.deleteOlderThan"
+ ng-change="deleteOldLogs.reloadDbStats()"
+ title="{{ 'PrivacyManager_DeleteLogsOlderThan'|translate|e('html_attr') }} ({{ 'Intl_PeriodDays'|translate }})"
+ value="{{ deleteData.config.delete_logs_older_than }}"
+ inline-help="{{ 'PrivacyManager_LeastDaysInput'|translate("1")|e('html_attr') }}">
</div>
</div>
- <h2 id="deleteReportsAnchor" class="secondary">{{ 'PrivacyManager_DeleteOldArchivedReports'|translate }}</h2>
+ <div piwik-save-button onconfirm="deleteOldLogs.save()" saving="deleteOldLogs.isLoading"></div>
+ </div>
+ </div>
+
+ <div piwik-content-block id="deleteReportsAnchor"
+ content-title="{{ 'PrivacyManager_DeleteOldArchivedReports'|translate|e('html_attr') }}">
+
+ <div piwik-form
+ ng-controller="DeleteOldReportsController as deleteReports"
+ id="formDeleteSettings">
- <div id="deleteReportsSettingEnabled" class="form-group">
- <label>
- {{ 'PrivacyManager_UseDeleteReports'|translate }}
- </label>
- <div class="form-help">
- {{ 'PrivacyManager_DeleteReportsDetailedInfo'|translate('archive_numeric_*','archive_blob_*') }}
+ <div id="deleteReportsSettingEnabled">
+
+ <div piwik-field uicontrol="checkbox" name="deleteReportsEnable"
+ ng-model="deleteReports.enabled"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'PrivacyManager_UseDeleteReports'|translate|e('html_attr') }}"
+ value="{{ deleteData.config.delete_reports_enable }}"
+ inline-help="{{ 'PrivacyManager_DeleteReportsDetailedInfo'|translate('archive_numeric_*','archive_blob_*')|e('html_attr') }}">
</div>
- <label class="radio">
- <input id="deleteReportsEnable-1" type="radio" name="deleteReportsEnable" value="1" {% if deleteData.config.delete_reports_enable == '1' %}checked="checked"{% endif %} />
- {{ 'General_Yes'|translate }}
- </label>
- <label class="radio">
- <input class="indented-radio-button" id="deleteReportsEnable-2" type="radio" name="deleteReportsEnable" value="0" {% if deleteData.config.delete_reports_enable == '0' %}checked="checked"{% endif %}/>
- {{ 'General_No'|translate }}
- </label>
- <div class="clearfix"><br/></div>
- <div class="alert alert-warning" style="width: 40%;">
+
+ <div class="alert alert-warning" style="width: 50%;">
{% set deleteOldLogs %}{{ 'PrivacyManager_UseDeleteLog'|translate }}{% endset %}
- {{ 'PrivacyManager_DeleteReportsInfo'|translate('<em>','</em>')|raw }}
- <span id='deleteOldReportsMoreInfo'><br/><br/>
+ {{ 'PrivacyManager_DeleteReportsInfo'|translate('','')|raw }}
+ <span ng-show="deleteReports.enabled">
+ <br/><br/>
{{ 'PrivacyManager_DeleteReportsInfo2'|translate(deleteOldLogs) }}<br/><br/>
- {{ 'PrivacyManager_DeleteReportsInfo3'|translate(deleteOldLogs) }}</span>
+ {{ 'PrivacyManager_DeleteReportsInfo3'|translate(deleteOldLogs) }}
+ </span>
</div>
+
</div>
- <div id="deleteReportsSettings">
- <div class="form-group">
- <label for="deleteReportsOlderThan">
- {{ 'PrivacyManager_DeleteReportsOlderThan'|translate }}
- </label>
- <div class="form-help">
- {{ 'PrivacyManager_LeastMonthsInput'|translate("3") }}
- </div>
- <div class="input-group">
- <input type="text" id="deleteReportsOlderThan" value="{{ deleteData.config.delete_reports_older_than }}" name="deleteReportsOlderThan"/>
- <span class="input-group-addon">{{ 'Intl_PeriodMonths'|translate }}</span>
- </div>
+ <div id="deleteReportsSettings" ng-show="deleteReports.enabled">
+
+ <div piwik-field uicontrol="text" name="deleteReportsOlderThan"
+ ng-model="deleteReports.deleteOlderThan"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'PrivacyManager_DeleteReportsOlderThan'|translate|e('html_attr') }} ({{ 'Intl_PeriodMonths'|translate }})"
+ value="{{ deleteData.config.delete_reports_older_than }}"
+ inline-help="{{ 'PrivacyManager_LeastMonthsInput'|translate("3")|e('html_attr') }}">
</div>
- <div class="form-group">
- <label class="checkbox">
- <input id="deleteReportsKeepBasic" type="checkbox" name="deleteReportsKeepBasic" value="1"
- {% if deleteData.config.delete_reports_keep_basic_metrics %}checked="checked"{% endif %}>
- {{ 'PrivacyManager_KeepBasicMetrics'|translate }}
- <span class="form-description">{{ 'General_Recommended'|translate }}</span>
- </label>
+
+ <div piwik-field uicontrol="checkbox" name="deleteReportsKeepBasic"
+ ng-model="deleteReports.keepBasic"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'PrivacyManager_KeepBasicMetrics'|translate|e('html_attr') }} ({{ 'General_Recommended'|translate|e('html_attr') }})"
+ value="{{ deleteData.config.delete_reports_keep_basic_metrics }}"
+ inline-help="{{ 'PrivacyManager_DeleteReportsDetailedInfo'|translate('archive_numeric_*','archive_blob_*')|e('html_attr') }}">
</div>
+
<h3>
{{ 'PrivacyManager_KeepDataFor'|translate }}
</h3>
- <div class="form-group">
- <label class="checkbox">
- <input id="deleteReportsKeepDay" type="checkbox" name="deleteReportsKeepDay" value="1"
- {% if deleteData.config.delete_reports_keep_day_reports %}checked="checked"{% endif %}>
- {{ 'General_DailyReports'|translate }}
- </label>
- <label class="checkbox">
- <input type="checkbox" name="deleteReportsKeepWeek" value="1" id="deleteReportsKeepWeek"
- {% if deleteData.config.delete_reports_keep_week_reports %}checked="checked"{% endif %}>
- {{ 'General_WeeklyReports'|translate }}
- </label>
- <label class="checkbox">
- <input type="checkbox" name="deleteReportsKeepMonth" value="1" id="deleteReportsKeepMonth"
- {% if deleteData.config.delete_reports_keep_month_reports %}checked="checked"{% endif %}>
- {{ 'General_MonthlyReports'|translate }}
- <span class="form-description">{{ 'General_Recommended'|translate }}</span>
- </label>
- <label class="checkbox">
- <input type="checkbox" name="deleteReportsKeepYear" value="1" id="deleteReportsKeepYear"
- {% if deleteData.config.delete_reports_keep_year_reports %}checked="checked"{% endif %}>
- {{ 'General_YearlyReports'|translate }}
- <span class="form-description">{{ 'General_Recommended'|translate }}</span>
- </label>
- <label class="checkbox">
- <input type="checkbox" name="deleteReportsKeepRange" value="1" id="deleteReportsKeepRange"
- {% if deleteData.config.delete_reports_keep_range_reports %}checked="checked"{% endif %}>
- {{ 'General_RangeReports'|translate }}
- </label>
- <label class="checkbox">
- <input type="checkbox" name="deleteReportsKeepSegments" value="1" id="deleteReportsKeepSegments"
- {% if deleteData.config.delete_reports_keep_segment_reports %}checked="checked"{% endif %}>
- {{ 'PrivacyManager_KeepReportSegments'|translate }}
- </label>
+ <div>
+
+ <div piwik-field uicontrol="checkbox" name="deleteReportsKeepDay"
+ ng-model="deleteReports.keepDataForDay"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'General_DailyReports'|translate|e('html_attr') }}"
+ value="{{ deleteData.config.delete_reports_keep_day_reports }}">
+ </div>
+ <div piwik-field uicontrol="checkbox" name="deleteReportsKeepWeek"
+ ng-model="deleteReports.keepDataForWeek"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'General_WeeklyReports'|translate|e('html_attr') }}"
+ value="{{ deleteData.config.delete_reports_keep_week_reports }}">
+ </div>
+ <div piwik-field uicontrol="checkbox" name="deleteReportsKeepMonth"
+ ng-model="deleteReports.keepDataForMonth"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'General_MonthlyReports'|translate|e('html_attr') }} ({{ 'General_Recommended'|translate|e('html_attr') }})"
+ value="{{ deleteData.config.delete_reports_keep_month_reports }}">
+ </div>
+ <div piwik-field uicontrol="checkbox" name="deleteReportsKeepYear"
+ ng-model="deleteReports.keepDataForYear"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'General_YearlyReports'|translate|e('html_attr') }} ({{ 'General_Recommended'|translate|e('html_attr') }})"
+ value="{{ deleteData.config.delete_reports_keep_year_reports }}">
+ </div>
+ <div piwik-field uicontrol="checkbox" name="deleteReportsKeepRange"
+ ng-model="deleteReports.keepDataForRange"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'General_RangeReports'|translate|e('html_attr') }}"
+ value="{{ deleteData.config.delete_reports_keep_range_reports }}">
+ </div>
+ <div piwik-field uicontrol="checkbox" name="deleteReportsKeepSegments"
+ ng-model="deleteReports.keepDataForSegments"
+ ng-change="deleteReports.reloadDbStats()"
+ title="{{ 'PrivacyManager_KeepReportSegments'|translate|e('html_attr') }}"
+ value="{{ deleteData.config.delete_reports_keep_segment_reports }}">
+ </div>
</div>
</div>
+ <div piwik-save-button onconfirm="deleteReports.save()" saving="deleteReports.isLoading"></div>
+ </div>
+ </div>
+
+ <div piwik-form
+ ng-controller="ScheduleReportDeletionController as reportDeletionSchedule"
+ id="formDeleteSettings">
- <h2 for="deleteLowestInterval" id="scheduleSettingsHeadline">
- {{ 'PrivacyManager_DeleteSchedulingSettings'|translate }}
- </h2>
- <div id="deleteSchedulingSettings" class="form-group">
- <div class="form-help">
+ <div piwik-content-block id="scheduleSettingsHeadline"
+ ng-show="reportDeletionSchedule.model.isEitherDeleteSectionEnabled()"
+ content-title="{{ 'PrivacyManager_DeleteSchedulingSettings'|translate|e('html_attr') }}">
+
+ <div id="deleteSchedulingSettings">
+ <div id="deleteSchedulingSettingsInlineHelp" class="inline-help-node">
{% if deleteData.lastRun %}<strong>{{ 'PrivacyManager_LastDelete'|translate }}:</strong>
{{ deleteData.lastRunPretty }}
<br/>
@@ -243,89 +228,95 @@
{{ deleteData.nextRunPretty|raw }}
<br/>
<br/>
- <em><a id="purgeDataNowLink" href="#">{{ 'PrivacyManager_PurgeNow'|translate }}</a></em>
- <span class="loadingPiwik" style="display:none;"><img
- src="./plugins/Morpheus/images/loading-blue.gif"/> {{ 'PrivacyManager_PurgingData'|translate }}</span>
- <span id="db-purged-message" style="display: none;"><em>{{ 'PrivacyManager_DBPurged'|translate }}</em></span>
+ <a id="purgeDataNowLink" href="#"
+ ng-show="reportDeletionSchedule.showPurgeNowLink"
+ ng-click="reportDeletionSchedule.executeDataPurgeNow()">{{ 'PrivacyManager_PurgeNow'|translate }}</a>
+
+ <div piwik-activity-indicator
+ loading-message="'{{ 'PrivacyManager_PurgingData'|translate|e('html_attr') }}'"
+ loading="reportDeletionSchedule.loadingDataPurge"></div>
+ <span id="db-purged-message"
+ ng-show="reportDeletionSchedule.dataWasPurged"
+ >{{ 'PrivacyManager_DBPurged'|translate }}</span>
</div>
- <label>{{ 'PrivacyManager_DeleteDataInterval'|translate }}</label>
- <select id="deleteLowestInterval" name="deleteLowestInterval">
- <option {% if deleteData.config.delete_logs_schedule_lowest_interval == '1' %} selected="selected" {% endif %}
- value="1"> {{ 'Intl_PeriodDay'|translate }}</option>
- <option {% if deleteData.config.delete_logs_schedule_lowest_interval == '7' %} selected="selected" {% endif %}
- value="7">{{ 'Intl_PeriodWeek'|translate }}</option>
- <option {% if deleteData.config.delete_logs_schedule_lowest_interval == '30' %} selected="selected" {% endif %}
- value="30">{{ 'Intl_PeriodMonth'|translate }}</option>
- </select>
+ <div piwik-field uicontrol="select" name="deleteLowestInterval"
+ ng-model="reportDeletionSchedule.deleteLowestInterval"
+ options="{{ scheduleDeletionOptions|json_encode }}"
+ title="{{ 'PrivacyManager_DeleteDataInterval'|translate|e('html_attr') }}"
+ value="{{ deleteData.config.delete_logs_schedule_lowest_interval }}"
+ inline-help="#deleteSchedulingSettingsInlineHelp">
+ </div>
</div>
- <h3 id="databaseSizeHeadline">
- {{ 'PrivacyManager_ReportsDataSavedEstimate'|translate }}
- </h3>
- <div {% if deleteData.config.delete_reports_enable == '0' and deleteData.config.delete_logs_enable == '0' %}style="display:none;"{% endif %}
- id="deleteDataEstimateSect" class="form-group">
- {% if deleteData.config.enable_auto_database_size_estimate == '0' %}
+ <div id="deleteDataEstimateSect" class="form-group row">
+
+ <h3 class="col s12" id="databaseSizeHeadline">
+ {{ 'PrivacyManager_ReportsDataSavedEstimate'|translate }}
+ </h3>
+ <div class="col s12 m6">
+ <div id="deleteDataEstimate" ng-show="reportDeletionSchedule.model.showEstimate"
+ ng-bind-html="reportDeletionSchedule.model.estimation"></div>
+ &nbsp;
+ <div piwik-activity-indicator loading="reportDeletionSchedule.model.loadingEstimation"></div>
+ </div>
+ <div class="col s12 m6">
+ {% if deleteData.config.enable_auto_database_size_estimate == '0' %}
<div class="form-help">
- <a id="getPurgeEstimateLink" href="#">{{ 'PrivacyManager_GetPurgeEstimate'|translate }}</a>
+ <a id="getPurgeEstimateLink"
+ ng-click="reportDeletionSchedule.model.reloadDbStats(true)"
+ href="#">{{ 'PrivacyManager_GetPurgeEstimate'|translate }}</a>
</div>
- {% endif %}
- <div id="deleteDataEstimate"></div>
- <span class="loadingPiwik" style="display:none;">
- <img src="./plugins/Morpheus/images/loading-blue.gif"/> {{ 'General_LoadingData'|translate }}
- </span>
+ {% endif %}
+ </div>
</div>
- <input type="button" value="{{ 'General_Save'|translate }}" id="deleteLogSettingsSubmit" class="submit"/>
-
- </form>
+ <div piwik-save-button onconfirm="reportDeletionSchedule.save()" saving="reportDeletionSchedule.isLoading"></div>
+ </div>
{% endif %}
-
- <h2 id="DNT">{{ 'PrivacyManager_DoNotTrack_SupportDNTPreference'|translate }}</h2>
+</div>
+<div piwik-content-block
+ id="DNT"
+ content-title="{{ 'PrivacyManager_DoNotTrack_SupportDNTPreference'|translate|e('html_attr') }}">
<p>
{% if dntSupport %}
- {% set action='deactivateDoNotTrack' %}
- {% set nonce=deactivateNonce %}
<strong>{{ 'PrivacyManager_DoNotTrack_Enabled'|translate }}</strong>
<br/>
{{ 'PrivacyManager_DoNotTrack_EnabledMoreInfo'|translate }}
{% else %}
- {% set action='activateDoNotTrack' %}
- {% set nonce=activateNonce %}
{{ 'PrivacyManager_DoNotTrack_Disabled'|translate }} {{ 'PrivacyManager_DoNotTrack_DisabledMoreInfo'|translate }}
{% endif %}
</p>
- <div class="form-group">
- <div class="form-help">
- {{ 'PrivacyManager_DoNotTrack_Description'|translate }}
+
+ <div piwik-form ng-controller="DoNotTrackPreferenceController as doNotTrack">
+
+ {# {{ {'module':'PrivacyManager','nonce':nonce,'action':action} | urlRewriteWithParameters }}#DNT #}
+ <div piwik-field uicontrol="radio" name="doNotTrack"
+ ng-model="doNotTrack.enabled"
+ options="{{ doNotTrackOptions|json_encode }}"
+ value="{% if dntSupport %}1{% else %}0{% endif %}"
+ inline-help="{{ 'PrivacyManager_DoNotTrack_Description'|translate|e('html_attr') }}">
</div>
- <a class="btn" href='{{ {'module':'PrivacyManager','nonce':nonce,'action':action} | urlRewriteWithParameters }}#DNT'>
- {% if dntSupport %}
- {{ 'PrivacyManager_DoNotTrack_Disable'|translate }}
- {% else %}
- {{ 'PrivacyManager_DoNotTrack_Enable'|translate }}
- {% endif %}
- </a>
- {% if dntSupport %}
- ({{ 'General_NotRecommended'|translate }})
- {% else %}
- ({{ 'General_Recommended'|translate }})
- {% endif %}
+ <div piwik-save-button onconfirm="doNotTrack.save()" saving="doNotTrack.isLoading"></div>
+
</div>
-{% endif %}
-<h2 id="optOutAnchor">{{ 'CoreAdminHome_OptOutForYourVisitors'|translate }}</h2>
-<p>
- {{ 'CoreAdminHome_OptOutExplanation'|translate }}
- {% set optOutUrl %}{{ piwikUrl }}index.php?module=CoreAdminHome&action=optOut&language={{ language }}{% endset %}
- {% set iframeOptOut %}<iframe style="border: 0; height: 200px; width: 600px;" src="{{ optOutUrl }}"></iframe>{% endset %}
-</p>
-<pre>{{ iframeOptOut|e('html') }}</pre>
-<p>
- {{ 'CoreAdminHome_OptOutExplanationBis'|translate("<a href='" ~ optOutUrl ~ "' rel='noreferrer' target='_blank'>","</a>")|raw }}
-</p>
-
-<div style="height:100px;"></div>
+
+{% endif %}
+</div>
+<div piwik-content-block
+ id="optOutAnchor"
+ content-title="{{ 'CoreAdminHome_OptOutForYourVisitors'|translate|e('html_attr') }}">
+ <p>
+ {{ 'CoreAdminHome_OptOutExplanation'|translate }}
+ {% set optOutUrl %}{{ piwikUrl }}index.php?module=CoreAdminHome&action=optOut&language={{ language }}{% endset %}
+ {% set iframeOptOut %}<iframe style="border: 0; height: 200px; width: 600px;" src="{{ optOutUrl }}"></iframe>{% endset %}
+ </p>
+ <pre piwik-select-on-focus>{{ iframeOptOut|e('html') }}</pre>
+ <p>
+ {{ 'CoreAdminHome_OptOutExplanationBis'|translate("<a href='" ~ optOutUrl ~ "' rel='noreferrer' target='_blank'>","</a>")|raw }}
+ </p>
+</div>
{% endblock %}
diff --git a/plugins/Provider/Updates/3.0.0-b1.php b/plugins/Provider/Updates/3.0.0-b1.php
index 27cae67555..9673905689 100644
--- a/plugins/Provider/Updates/3.0.0-b1.php
+++ b/plugins/Provider/Updates/3.0.0-b1.php
@@ -7,7 +7,7 @@
*
*/
-namespace Piwik\Plugins\Provider\Updates;
+namespace Piwik\Plugins\Provider;
use Piwik\Updater;
use Piwik\Updates as PiwikUpdates;
diff --git a/plugins/QueuedTracking b/plugins/QueuedTracking
-Subproject 093d4715746147fe9c50c29705596ed4ee38673
+Subproject 73e54678385bb18850c3d2b453e77fcffa449d4
diff --git a/plugins/Referrers/Controller.php b/plugins/Referrers/Controller.php
index 1b258f4c6a..4554acd35a 100644
--- a/plugins/Referrers/Controller.php
+++ b/plugins/Referrers/Controller.php
@@ -74,9 +74,8 @@ class Controller extends \Piwik\Plugin\Controller
$distinctMetrics = $this->addEvolutionPropertiesToView($prettyDate, $distinctMetrics, $prettyLastPeriodDate, $previousValues);
}
-
/** @var Sparklines $view */
- $view = ViewDataTable\Factory::build(Sparklines::ID, $api = false, $controller = false, $force = true, $loadUserParams = false);
+ $view = ViewDataTable\Factory::build(Sparklines::ID, $api = '', $controller = '', $force = true, $loadUserParams = false);
// DIRECT ENTRY
$metrics['visitorsFromDirectEntry'] = $numberFormatter->formatNumber($metrics['visitorsFromDirectEntry']);
@@ -392,16 +391,16 @@ function DisplayTopKeywords($url = "")
';
$jsonRequest = str_replace('format=php', 'format=json', $api);
- echo "<p>This widget is designed to work in your website directly.
+ echo "<p style='padding: 0 12px;'>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>
+ <p style='padding: 0 12px;'>
<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 rel='noreferrer' target='_blank' href='$topPageUrl'>$topPageUrl</a>,
in format JSON: you would dynamically fetch the data using <a rel='noreferrer' 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.
+ <p style='padding: 0 12px;'><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;'>";
+ echo "<div style='width:400px;margin:10px 10px 0 10px;padding:10px;border:1px solid #333;'>";
function DisplayTopKeywords($url = "", $api)
{
// Do not spend more than 1 second fetching the data
@@ -433,12 +432,12 @@ function DisplayTopKeywords($url = "")
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>
+ <p style='padding: 0 12px;'>Here is the PHP function that you can paste in your pages:</P>
+ <textarea style='padding: 0 12px;height:auto;width:auto;margin-left:12px;' cols=60 rows=8>&lt;?php\n" . htmlspecialchars($code) . "\n DisplayTopKeywords();</textarea>
";
echo "
- <p><strong>Notes</strong>: You can for example edit the code to to make the Top search keywords link to your Website search result pages.
+ <p style='padding: 12px;'><strong>Notes</strong>: 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>
";
diff --git a/plugins/Referrers/Referrers.php b/plugins/Referrers/Referrers.php
index d262c79224..9d4d03a4a1 100644
--- a/plugins/Referrers/Referrers.php
+++ b/plugins/Referrers/Referrers.php
@@ -106,6 +106,6 @@ class Referrers extends \Piwik\Plugin
$label = strtolower(Piwik::translate($indexTranslation));
// return html that displays it as grey & italic
- return '<span class="datatable-label-category"><em>(' . $label . ')</em></span>';
+ return '<span class="datatable-label-category">(' . $label . ')</span>';
}
}
diff --git a/plugins/SEO/Widgets/GetRank.php b/plugins/SEO/Widgets/GetRank.php
index 521bf1859c..be22d61eec 100644
--- a/plugins/SEO/Widgets/GetRank.php
+++ b/plugins/SEO/Widgets/GetRank.php
@@ -41,16 +41,15 @@ class GetRank extends \Piwik\Widget\Widget
}
$dataTable = API::getInstance()->getRank($url);
-
- $view = new View('@SEO/getRank');
- $view->urlToRank = Url::getHostFromUrl($url);
-
+
/** @var \Piwik\DataTable\Renderer\Php $renderer */
$renderer = Renderer::factory('php');
$renderer->setSerialize(false);
- $view->ranks = $renderer->render($dataTable);
- return $view->render();
+ return $this->renderTemplate('getRank', array(
+ 'urlToRank' => Url::getHostFromUrl($url),
+ 'ranks' => $renderer->render($dataTable)
+ ));
}
}
diff --git a/plugins/SEO/templates/getRank.twig b/plugins/SEO/templates/getRank.twig
index 6eb025d7e4..a00094e2df 100644
--- a/plugins/SEO/templates/getRank.twig
+++ b/plugins/SEO/templates/getRank.twig
@@ -2,18 +2,20 @@
<script type="text/javascript" src="plugins/SEO/javascripts/rank.js"></script>
<form method="post" style="padding: 8px;">
- <div align="left" class="mediumtext">
- {{ 'Installation_SetupWebSiteURL'|translate|capitalize }}
- <input type="text" id="seoUrl" size="15" value="{{ urlToRank }}" class="textbox"/>
- <span style="padding-left:2px;">
- <input type="submit" id="rankbutton" value="{{ 'SEO_Rank'|translate }}"/>
- </span>
+ <div align="left" class="row">
+ <div class="col s8 input-field">
+ <label for="seoUrl" class="active">{{ 'Installation_SetupWebSiteURL'|translate|capitalize }}</label>
+ <input type="text" id="seoUrl" size="15" value="{{ urlToRank }}" class="textbox "/>
+ </div>
+ <div class="col s4">
+ <input type="submit" class="btn btn-small" id="rankbutton" value="{{ 'SEO_Rank'|translate }}"/>
+ </div>
</div>
{% import "ajaxMacros.twig" as ajax %}
{{ ajax.LoadingDiv('ajaxLoadingSEO') }}
- <div id="rankStats" align="left" style="margin-top:10px;">
+ <div id="rankStats" align="left" style="margin-top:10px;margin-left: 5px;">
{% if ranks is empty %}
{{ 'General_Error'|translate }}
{% else %}
diff --git a/plugins/ScheduledReports/Controller.php b/plugins/ScheduledReports/Controller.php
index 3192a3cfc4..a58faed74e 100644
--- a/plugins/ScheduledReports/Controller.php
+++ b/plugins/ScheduledReports/Controller.php
@@ -30,17 +30,27 @@ class Controller extends \Piwik\Plugin\Controller
// get report types
$reportTypes = API::getReportTypes();
+ $reportTypeOptions = array();
+ foreach ($reportTypes as $reportType => $icon) {
+ $reportTypeOptions[$reportType] = mb_strtoupper($reportType);
+ }
$view->reportTypes = $reportTypes;
+ $view->reportTypeOptions = $reportTypeOptions;
$view->defaultReportType = self::DEFAULT_REPORT_TYPE;
$view->defaultReportFormat = ScheduledReports::DEFAULT_REPORT_FORMAT;
$view->displayFormats = ScheduledReports::getDisplayFormats();
$reportsByCategoryByType = array();
+ $reportFormatsByReportTypeOptions = array();
$reportFormatsByReportType = array();
$allowMultipleReportsByReportType = array();
foreach ($reportTypes as $reportType => $reportTypeIcon) {
// get report formats
$reportFormatsByReportType[$reportType] = API::getReportFormats($reportType);
+ $reportFormatsByReportTypeOptions[$reportType] = $reportFormatsByReportType[$reportType];
+ foreach ($reportFormatsByReportTypeOptions[$reportType] as $type => $icon) {
+ $reportFormatsByReportTypeOptions[$reportType][$type] = mb_strtoupper($type);
+ }
$allowMultipleReportsByReportType[$reportType] = API::allowMultipleReports($reportType);
// get report metadata
@@ -53,6 +63,7 @@ class Controller extends \Piwik\Plugin\Controller
}
$view->reportsByCategoryByReportType = $reportsByCategoryByType;
$view->reportFormatsByReportType = $reportFormatsByReportType;
+ $view->reportFormatsByReportTypeOptions = $reportFormatsByReportTypeOptions;
$view->allowMultipleReportsByReportType = $allowMultipleReportsByReportType;
$reports = array();
@@ -78,7 +89,9 @@ class Controller extends \Piwik\Plugin\Controller
$view->segmentEditorActivated = false;
if (API::isSegmentEditorActivated()) {
- $savedSegmentsById = array();
+ $savedSegmentsById = array(
+ '' => Piwik::translate('SegmentEditor_DefaultAllVisits')
+ );
foreach (APISegmentEditor::getInstance()->getAll($this->idSite) as $savedSegment) {
$savedSegmentsById[$savedSegment['idsegment']] = $savedSegment['name'];
}
diff --git a/plugins/ScheduledReports/ScheduledReports.php b/plugins/ScheduledReports/ScheduledReports.php
index f306dd8d05..b966706cfc 100644
--- a/plugins/ScheduledReports/ScheduledReports.php
+++ b/plugins/ScheduledReports/ScheduledReports.php
@@ -128,7 +128,8 @@ class ScheduledReports extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
- $jsFiles[] = "plugins/ScheduledReports/javascripts/pdf.js";
+ $jsFiles[] = "plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.controller.js";
+ $jsFiles[] = "plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.directive.js";
}
public function getStylesheetFiles(&$stylesheets)
diff --git a/plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.controller.js b/plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.controller.js
new file mode 100644
index 0000000000..ed50d95314
--- /dev/null
+++ b/plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.controller.js
@@ -0,0 +1,194 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ManageScheduledReportController', ManageScheduledReportController);
+
+ ManageScheduledReportController.$inject = ['piwik', '$timeout'];
+
+ function ManageScheduledReportController(piwik, $timeout) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+
+ function scrollToTop()
+ {
+ piwikHelper.lazyScrollTo(".emailReports", 200);
+ }
+
+ function updateParameters(reportType, report)
+ {
+ if (updateReportParametersFunctions && updateReportParametersFunctions[reportType]) {
+ updateReportParametersFunctions[reportType](report);
+ }
+ }
+
+ function resetParameters(reportType, report)
+ {
+ if (resetReportParametersFunctions && resetReportParametersFunctions[reportType]) {
+ resetReportParametersFunctions[reportType](report)
+ }
+ }
+
+ 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];
+ updateParameters(report.type, report);
+ self.saveButtonTitle = ReportPlugin.updateReportString;
+ } else {
+ self.saveButtonTitle = ReportPlugin.createReportString;
+ resetParameters(report.type, report);
+ }
+
+ $('[name=reportsList] input').prop('checked', false);
+
+ var key;
+ for (key in report.reports) {
+ $('.' + report.type + ' [report-unique-id=' + report.reports[key] + ']').prop('checked', 'checked');
+ }
+
+ report['format' + report.type] = report.format;
+
+ self.report = report;
+ self.report.description = piwik.helper.htmlDecode(self.report.description);
+ self.editingReportId = idReport;
+ }
+
+ function getReportAjaxRequest(idReport, defaultApiMethod) {
+ scrollToTop();
+
+ var ajaxHandler = new ajaxHelper();
+
+ var parameters = {module: 'API', method: defaultApiMethod, format: 'json'};
+ if (idReport == 0) {
+ parameters.method = 'ScheduledReports.addReport';
+ }
+
+ ajaxHandler.addParams(parameters, 'GET');
+
+ return ajaxHandler;
+ }
+
+ function fadeInOutSuccessMessage(selector, message) {
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(message, {
+ placeat: selector,
+ context: 'success',
+ noclear: true,
+ type: 'toast',
+ style: {display: 'inline-block', marginTop: '10px'},
+ id: 'scheduledReportSuccess'
+ });
+
+ piwikHelper.refreshAfter(2);
+ }
+
+ // Click Add/Update Submit
+ this.submitReport = function () {
+ var idReport = this.editingReportId;
+ var apiParameters = {};
+ apiParameters.idReport = idReport;
+ apiParameters.description = this.report.description;
+ apiParameters.idSegment = this.report.idsegment;
+ apiParameters.reportType = this.report.type;
+ apiParameters.reportFormat = this.report['format' + this.report.type];
+
+ var period = self.report.period;
+ var hour = self.report.hour;
+
+ 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[this.report.type](this.report);
+
+ var ajaxHandler = getReportAjaxRequest(idReport, 'ScheduledReports.updateReport');
+ ajaxHandler.addParams(apiParameters, 'POST');
+ ajaxHandler.addParams({period: period}, 'GET');
+ ajaxHandler.addParams({hour: hour}, 'GET');
+ ajaxHandler.redirectOnSuccess();
+ ajaxHandler.setLoadingElement();
+ if (idReport) {
+ ajaxHandler.setCallback(function (response) {
+
+ fadeInOutSuccessMessage('#reportUpdatedSuccess', _pk_translate('ScheduledReports_ReportUpdated'));
+ });
+ }
+ ajaxHandler.send();
+ return false;
+ };
+
+ this.changedReportType = function () {
+ resetParameters(this.report.type, this.report);
+ };
+
+ // Email now
+ this.sendReportNow = function (idReport) {
+ var ajaxHandler = getReportAjaxRequest(idReport, 'ScheduledReports.sendReport');
+ ajaxHandler.addParams({idReport: idReport, force: true}, 'POST');
+ ajaxHandler.setLoadingElement();
+ ajaxHandler.setCallback(function (response) {
+ fadeInOutSuccessMessage('#reportSentSuccess', _pk_translate('ScheduledReports_ReportSent'));
+ });
+ ajaxHandler.send();
+ };
+
+ // Delete Report
+ this.deleteReport = function (idReport) {
+ function onDelete() {
+ var ajaxHandler = getReportAjaxRequest(idReport, 'ScheduledReports.deleteReport');
+ ajaxHandler.addParams({idReport: idReport}, 'POST');
+ ajaxHandler.redirectOnSuccess();
+ ajaxHandler.setLoadingElement();
+ ajaxHandler.send();
+ }
+
+ piwikHelper.modalConfirm('#confirm', {yes: onDelete});
+ };
+
+ this.showListOfReports = function (shouldScrollToTop) {
+ this.showReportsList = true;
+ this.showReportForm = false;
+ piwik.helper.hideAjaxError();
+
+ if (typeof shouldScrollToTop === 'undefined' || !shouldScrollToTop) {
+ scrollToTop();
+ }
+ };
+
+ this.showAddEditForm = function () {
+ this.showReportsList = false;
+ this.showReportForm = true;
+ };
+
+ this.createReport = function () {
+ this.showAddEditForm();
+ formSetEditReport(/*idReport = */0);
+ }
+
+ this.editReport = function (reportId) {
+ this.showAddEditForm();
+ formSetEditReport(reportId);
+ };
+
+ this.showListOfReports(false);
+ }
+})(); \ No newline at end of file
diff --git a/plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.directive.js b/plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.directive.js
new file mode 100644
index 0000000000..d130fc61f7
--- /dev/null
+++ b/plugins/ScheduledReports/angularjs/manage-scheduled-report/manage-scheduled-report.directive.js
@@ -0,0 +1,30 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+var getReportParametersFunctions = Object();
+var updateReportParametersFunctions = Object();
+var resetReportParametersFunctions = Object();
+
+/**
+ * Usage:
+ * <div piwik-manage-scheduled-report>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikManageScheduledReport', piwikManageScheduledReport);
+
+ piwikManageScheduledReport.$inject = ['piwik'];
+
+ function piwikManageScheduledReport(piwik){
+
+ return {
+ restrict: 'A',
+ priority: 10,
+ controller: 'ManageScheduledReportController',
+ controllerAs: 'manageScheduledReport'
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/ScheduledReports/javascripts/pdf.js b/plugins/ScheduledReports/javascripts/pdf.js
deleted file mode 100644
index 2b86809517..0000000000
--- a/plugins/ScheduledReports/javascripts/pdf.js
+++ /dev/null
@@ -1,203 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-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_segment').find('option[value=' + report.idsegment + ']').prop('selected', 'selected');
- $('#report_type').find('option[value=' + report.type + ']').prop('selected', 'selected');
- $('#report_period').find('option[value=' + report.period + ']').prop('selected', 'selected');
- $('#report_hour').val(report.hour);
- $('[name=report_format].' + report.type + ' option[value=' + report.format + ']').prop('selected', 'selected');
-
- $('select[name=report_type]').change( toggleDisplayOptionsByFormat );
- $('select[name=report_format]').change( toggleDisplayOptionsByFormat );
-
- // When CSV is selected, hide "Display options"
- toggleDisplayOptionsByFormat();
-
- function toggleDisplayOptionsByFormat() {
- var selectorReportFormat = 'select[name=report_format].' + $('#report_type').val();
- var format = $(selectorReportFormat).val();
- var displayOptionsSelector = $('#row_report_display_options');
- if (format == 'csv' || format == 'sms') {
- displayOptionsSelector.hide();
- } else {
- displayOptionsSelector.show();
- }
- }
-
- $('[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(".emailReports>h2", 400);
- parameters.module = 'API';
- parameters.method = defaultApiMethod;
- if (idReport == 0) {
- parameters.method = 'ScheduledReports.addReport';
- }
- parameters.format = 'json';
- return parameters;
-}
-
-function toggleReportType(reportType) {
- resetReportParametersFunctions[reportType]();
- $('#report_type').find('option').each(function (index, type) {
- $('.' + $(type).val()).hide();
- });
- $('.' + reportType).show();
-}
-
-function fadeInOutSuccessMessage(selector, message) {
-
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
- notification.show(message, {
- placeat: selector,
- context: 'success',
- noclear: true,
- type: 'toast',
- style: {display: 'inline-block', marginTop: '10px'},
- id: 'usersManagerAccessUpdated'
- });
-
- piwikHelper.refreshAfter(2);
-}
-
-function initManagePdf() {
- // Click Add/Update Submit
- $('#addEditReport').submit(function () {
- var idReport = $('#report_idreport').val();
- var apiParameters = getReportAjaxRequest(idReport, 'ScheduledReports.updateReport');
- apiParameters.idReport = idReport;
- apiParameters.description = $('#report_description').val();
- apiParameters.idSegment = $('#report_segment').find('option:selected').val();
- apiParameters.reportType = $('#report_type').find('option:selected').val();
- apiParameters.reportFormat = $('[name=report_format].' + apiParameters.reportType + ' option:selected').val();
-
- var reports = [];
- $('[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');
- ajaxHandler.addParams({period: $('#report_period').find('option:selected').val()}, 'GET');
- ajaxHandler.addParams({hour: $('#report_hour').val()}, 'GET');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement();
- if (idReport) {
- ajaxHandler.setCallback(function (response) {
-
- fadeInOutSuccessMessage('#reportUpdatedSuccess', _pk_translate('ScheduledReports_ReportUpdated'));
- });
- }
- ajaxHandler.send(true);
- return false;
- });
-
- // Email now
- $('a[name=linkSendNow]').click(function () {
- var idReport = $(this).attr('idreport');
- var parameters = getReportAjaxRequest(idReport, 'ScheduledReports.sendReport');
- parameters.idReport = idReport;
- parameters.force = true;
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams(parameters, 'POST');
- ajaxHandler.setLoadingElement();
- ajaxHandler.setCallback(function (response) {
- fadeInOutSuccessMessage('#reportSentSuccess', _pk_translate('ScheduledReports_ReportSent'));
- });
- ajaxHandler.send(true);
- });
-
- // Delete Report
- $('.delete-report').click(function () {
- var idReport = $(this).attr('id');
-
- function onDelete() {
- var parameters = getReportAjaxRequest(idReport, 'ScheduledReports.deleteReport');
- parameters.idReport = idReport;
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams(parameters, 'POST');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement();
- ajaxHandler.send(true);
- }
-
- piwikHelper.modalConfirm('#confirm', {yes: onDelete});
- });
-
- // Edit Report click
- $('.edit-report').click(function () {
- var idReport = $(this).attr('id');
- formSetEditReport(idReport);
- $('.entityAddContainer').show();
- $('#entityEditContainer').hide();
- $(document).trigger('ScheduledReport.edit', {});
- });
-
- // Switch Report Type
- $('#report_type').change(function () {
- var reportType = $(this).val();
- toggleReportType(reportType);
- });
-
- // Add a Report click
- $('#add-report').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/ScheduledReports/lang/en.json b/plugins/ScheduledReports/lang/en.json
index a1ef702c9a..3b53fb3987 100644
--- a/plugins/ScheduledReports/lang/en.json
+++ b/plugins/ScheduledReports/lang/en.json
@@ -1,6 +1,6 @@
{
"ScheduledReports": {
- "AggregateReportsFormat": "(optional) Display options",
+ "AggregateReportsFormat": "Display options",
"AggregateReportsFormat_GraphsOnly": "Display Graphs only (no report tables)",
"AggregateReportsFormat_TablesAndGraphs": "Display Report tables and Graphs for all reports",
"AggregateReportsFormat_TablesOnly": "(default) Display Report tables (Graphs only for key metrics)",
diff --git a/plugins/ScheduledReports/stylesheets/scheduledreports.less b/plugins/ScheduledReports/stylesheets/scheduledreports.less
index 871b89da3c..47b9fbed05 100644
--- a/plugins/ScheduledReports/stylesheets/scheduledreports.less
+++ b/plugins/ScheduledReports/stylesheets/scheduledreports.less
@@ -1,11 +1,13 @@
.emailReports {
- .entityContainer {
- padding-top: 16px;
+ .entityTableContainer {
+ margin-top: 0;
}
-}
-#report_hour {
- height: 0.9em;
- padding: 0 0 0 5px;
- width: 35px;
+ a.withIcon {
+ display: inline-block;
+
+ img {
+ vertical-align: bottom;
+ }
+ }
}
diff --git a/plugins/ScheduledReports/templates/_addReport.twig b/plugins/ScheduledReports/templates/_addReport.twig
index 24f97ad19f..27cde7ae65 100644
--- a/plugins/ScheduledReports/templates/_addReport.twig
+++ b/plugins/ScheduledReports/templates/_addReport.twig
@@ -1,176 +1,147 @@
-<div class="entityAddContainer" style="display:none;">
+<div piwik-content-block
+ content-title="{{ 'ScheduledReports_CreateAndScheduleReport'|translate|e('html_attr') }}"
+ class="entityAddContainer"
+ ng-show="manageScheduledReport.showReportForm">
<div class='clear'></div>
- <form id='addEditReport'>
- <table class="dataTable entityTable">
- <thead>
- <tr class="first">
- <th colspan="2">{{ 'ScheduledReports_CreateAndScheduleReport'|translate }}</th>
- <tr>
- </thead>
- <tbody>
- <tr>
- <td class="first">{{ 'General_Website'|translate }} </td>
- <td style="width:650px;">
- {{ siteName|raw }}
- </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">
- {{ 'ScheduledReports_DescriptionOnFirstPage'|translate }}
- </div>
- </td>
- </tr>
- {% if segmentEditorActivated %}
- <tr>
- <td class="first">{{ 'SegmentEditor_ChooseASegment'|translate }} </td>
- <td>
- <select id='report_segment'>
- <option value="">{{ 'SegmentEditor_DefaultAllVisits'|translate }}</option>
- {% for savedSegmentId, savedSegmentName in savedSegmentsById %}
- <option value="{{ savedSegmentId }}">{{ savedSegmentName[:50] }}</option>
+ <form id='addEditReport' piwik-form ng-submit="manageScheduledReport.submitReport()">
+
+ <div piwik-field uicontrol="text" name="website"
+ title="{{ 'General_Website'|translate|e('html_attr') }}"
+ disabled="true"
+ value="{{ siteName|raw }}">
+ </div>
+
+ <div piwik-field uicontrol="textarea" name="report_description"
+ title="{{ 'General_Description'|translate|e('html_attr') }}"
+ ng-model="manageScheduledReport.report.description"
+ inline-help="{{ 'ScheduledReports_DescriptionOnFirstPage'|translate|e('html_attr') }}">
+ </div>
+
+ {% if segmentEditorActivated %}
+ <div id="reportSegmentInlineHelp" class="inline-help-node">
+ {% set SegmentEditor_DefaultAllVisits %}{{ 'SegmentEditor_DefaultAllVisits'|translate }}{% endset %}
+ {% set SegmentEditor_AddNewSegment %}{{ 'SegmentEditor_AddNewSegment'|translate }}{% endset %}
+ {{ 'ScheduledReports_Segment_Help'|translate('<a href="./" rel="noreferrer" target="_blank">','</a>',SegmentEditor_DefaultAllVisits,SegmentEditor_AddNewSegment)|raw }}
+ </div>
+
+ <div piwik-field uicontrol="select" name="report_segment"
+ ng-model="manageScheduledReport.report.idsegment"
+ options="{{ savedSegmentsById|json_encode }}"
+ title="{{ 'SegmentEditor_ChooseASegment'|translate|e('html_attr') }}"
+ inline-help="#reportSegmentInlineHelp">
+ </div>
+ {% endif %}
+
+ <div id="emailScheduleInlineHelp" class="inline-help-node">
+ {{ 'ScheduledReports_WeeklyScheduleHelp'|translate }}
+ <br/>
+ {{ 'ScheduledReports_MonthlyScheduleHelp'|translate }}
+ </div>
+
+ <div piwik-field uicontrol="select" name="report_period"
+ options="{{ periods|json_encode }}"
+ ng-model="manageScheduledReport.report.period"
+ title="{{ 'ScheduledReports_EmailSchedule'|translate|e('html_attr') }}"
+ inline-help="#emailScheduleInlineHelp">
+ </div>
+
+ <div piwik-field uicontrol="text" name="report_hour"
+ ng-model="manageScheduledReport.report.hour"
+ title="{{ 'ScheduledReports_ReportHour'|translate('X')|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="select" name="report_type"
+ options="{{ reportTypeOptions|json_encode }}"
+ ng-model="manageScheduledReport.report.type"
+ ng-change="manageScheduledReport.changedReportType()"
+ {% if reportTypes|length == 1 %}disabled="true"{% endif %}
+ title="{{ 'ScheduledReports_ReportType'|translate|e('html_attr') }}">
+ </div>
+
+ {% for reportType, reportFormats in reportFormatsByReportTypeOptions %}
+ <div piwik-field uicontrol="select" name="report_format"
+ class="{{ reportType }}"
+ ng-model="manageScheduledReport.report.format{{ reportType }}"
+ ng-show="manageScheduledReport.report.type == '{{ reportType }}'"
+ options="{{ reportFormats|json_encode }}"
+ title="{{ 'ScheduledReports_ReportFormat'|translate|e('html_attr') }}">
+ </div>
+ {% endfor %}
+
+ {{ postEvent("Template.reportParametersScheduledReports") }}
+
+ <div ng-show="manageScheduledReport.report.type == 'email' && manageScheduledReport.report.formatemail !== 'csv'">
+ <div piwik-field uicontrol="select" name="display_format" class="email"
+ ng-model="manageScheduledReport.report.displayFormat"
+ options="{{ displayFormats|json_encode }}"
+ introduction="{{ 'ScheduledReports_AggregateReportsFormat'|translate|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="checkbox" name="report_evolution_graph"
+ class="report_evolution_graph"
+ ng-model="manageScheduledReport.report.evolutionGraph"
+ ng-show="manageScheduledReport.report.displayFormat == '2' || manageScheduledReport.report.displayFormat == '3'"
+ title="{{ 'ScheduledReports_EvolutionGraph'|translate(5)|e('html_attr') }}">
+ </div>
+ </div>
+
+ <div class="row">
+ <h3 class="col s12">{{ 'ScheduledReports_ReportsIncluded'|translate }}</h3>
+ </div>
+
+ {% for reportType, reportsByCategory in reportsByCategoryByReportType %}
+ <div name='reportsList' class='row {{ reportType }}'
+ ng-show="manageScheduledReport.report.type == '{{ reportType }}'">
+
+ {% if allowMultipleReportsByReportType[reportType] %}
+ {% set reportInputType='checkbox' %}
+ {% else %}
+ {% set reportInputType='radio' %}
+ {% endif %}
+
+ {% set countCategory=0 %}
+
+ {% set newColumnAfter=(reportsByCategory|length + 1)//2 %}
+
+ <div class='col s12 m6'>
+ {% for category, reports in reportsByCategory %}
+ {% if countCategory >= newColumnAfter and newColumnAfter != 0 %}
+ {% set newColumnAfter=0 %}
+ </div>
+ <div class='col s12 m6'>
+ {% endif %}
+ <div class='reportCategory'>{{ category }}</div>
+ <ul class='listReports'>
+ {% for report in reports %}
+ <li>
+ <input type='{{ reportInputType }}' id="{{ reportType }}{{ report.uniqueId }}" report-unique-id='{{ report.uniqueId }}'
+ name='{{ reportType }}Reports'/>
+ <label for="{{ reportType }}{{ report.uniqueId }}">
+ {{ report.name|raw }}
+ {% if report.uniqueId=='MultiSites_getAll' %}
+ <div class="entityInlineHelp">{{ 'ScheduledReports_ReportIncludeNWebsites'|translate(countWebsites)
+ }}</div>
+ {% endif %}
+ </label>
+ </li>
{% endfor %}
- </select>
-
- <div class="entityInlineHelp">
- {% set SegmentEditor_DefaultAllVisits %}{{ 'SegmentEditor_DefaultAllVisits'|translate }}{% endset %}
- {% set SegmentEditor_AddNewSegment %}{{ 'SegmentEditor_AddNewSegment'|translate }}{% endset %}
- {{ 'ScheduledReports_Segment_Help'|translate('<a href="./" rel="noreferrer" target="_blank">','</a>',SegmentEditor_DefaultAllVisits,SegmentEditor_AddNewSegment)|raw }}
- </div>
- </td>
- </tr>
- {% endif %}
- <tr>
- <td class="first">{{ 'ScheduledReports_EmailSchedule'|translate }}</td>
- <td>
- <select id="report_period" class="inp">
- {% for periodId, period in periods %}
- <option value="{{ periodId }}">
- {{ period }}
- </option>
- {% endfor %}
- </select>
-
- <div class="entityInlineHelp">
- {{ 'ScheduledReports_WeeklyScheduleHelp'|translate }}
- <br/>
- {{ 'ScheduledReports_MonthlyScheduleHelp'|translate }}
- <br/>
- {{ 'ScheduledReports_ReportHour'|translate('<input type="text" id="report_hour" class="inp" size="2">')|raw }}
- </div>
- </td>
- </tr>
-
- <tr {% if reportTypes|length == 1 %}style="display:none;"{% endif %}>
- <td class="first">
- {{ 'ScheduledReports_ReportType'|translate }}
- </td>
- <td>
- <select id="report_type">
- {% for reportType, reportTypeIcon in reportTypes %}
- <option value="{{ reportType }}">{{ reportType|upper }}</option>
- {% endfor %}
- </select>
- </td>
- </tr>
-
- <tr>
- <td class="first">
- {{ 'ScheduledReports_ReportFormat'|translate }}
- </td>
-
- <td>
- {% for reportType, reportFormats in reportFormatsByReportType %}
- <select name='report_format' class='{{ reportType }}'>
- {% for reportFormat, reportFormatIcon in reportFormats %}
- <option value="{{ reportFormat }}">{{ reportFormat|upper }}</option>
- {% endfor %}
- </select>
+ {% set countCategory=countCategory+1 %}
+ </ul>
+ <br/>
{% endfor %}
- </td>
- </tr>
-
- {{ postEvent("Template.reportParametersScheduledReports") }}
-
- <tr class='email' id="row_report_display_options">
- <td class="first">
- {# ScheduledReports_AggregateReportsFormat should be named ScheduledReports_DisplayFormat #}
- {{ 'ScheduledReports_AggregateReportsFormat'|translate }}
- </td>
- <td>
- <select id="display_format">
- {% for formatValue, formatLabel in displayFormats %}
- <option {% if formatValue==1 %}selected{% endif %} value="{{ formatValue }}">{{ formatLabel }}</option>
- {% endfor %}
- </select>
-
- <div class='report_evolution_graph'>
- <br/>
- <input type="checkbox" id="report_evolution_graph"/>
- <label for="report_evolution_graph"><em>{{ 'ScheduledReports_EvolutionGraph'|translate(5) }}</em></label>
- </div>
- </td>
- </tr>
-
- <tr>
- <td class="first">{{ 'ScheduledReports_ReportsIncluded'|translate }}</td>
- <td>
- {% for reportType, reportsByCategory in reportsByCategoryByReportType %}
- <div name='reportsList' class='{{ reportType }}'>
-
- {% if allowMultipleReportsByReportType[reportType] %}
- {% set reportInputType='checkbox' %}
- {% else %}
- {% set reportInputType='radio' %}
- {% endif %}
-
- {% set countCategory=0 %}
-
- {% set newColumnAfter=(reportsByCategory|length + 1)//2 %}
-
- <div id='leftcolumn'>
- {% for category, reports in reportsByCategory %}
- {% if countCategory >= newColumnAfter and newColumnAfter != 0 %}
- {% set newColumnAfter=0 %}
- </div>
- <div id='rightcolumn'>
- {% endif %}
- <div class='reportCategory'>{{ category }}</div>
- <ul class='listReports'>
- {% for report in reports %}
- <li>
- <input type='{{ reportInputType }}' id="{{ reportType }}{{ report.uniqueId }}" report-unique-id='{{ report.uniqueId }}'
- name='{{ reportType }}Reports'/>
- <label for="{{ reportType }}{{ report.uniqueId }}">
- {{ report.name|raw }}
- {% if report.uniqueId=='MultiSites_getAll' %}
- <div class="entityInlineHelp">{{ 'ScheduledReports_ReportIncludeNWebsites'|translate(countWebsites)
- }}</div>
- {% endif %}
- </label>
- </li>
- {% endfor %}
- {% set countCategory=countCategory+1 %}
- </ul>
- <br/>
- {% endfor %}
- </div>
- </div>
- {% endfor %}
- </td>
- </tr>
+ </div>
+ </div>
+ {% endfor %}
+
+ <input type="hidden" id="report_idreport" ng-model="manageScheduledReport.editingReportId">
- </tbody>
- </table>
+ <div ng-value="manageScheduledReport.saveButtonTitle"
+ onconfirm="manageScheduledReport.submitReport()"
+ piwik-save-button></div>
- <input type="hidden" id="report_idreport" value="">
- <input type="submit" id="report_submit" name="submit" class="submit"/>
+ <div class='entityCancel'>
+ {{ 'General_OrCancel'|translate("<a class='entityCancelLink' ng-click='manageScheduledReport.showListOfReports()'>","</a>")|raw }}
+ </div>
</form>
- <div class='entityCancel'>
- {{ 'General_OrCancel'|translate("<a class='entityCancelLink'>","</a>")|raw }}
- </div>
</div>
diff --git a/plugins/ScheduledReports/templates/_listReports.twig b/plugins/ScheduledReports/templates/_listReports.twig
index 12dc5dd785..fee3178d63 100644
--- a/plugins/ScheduledReports/templates/_listReports.twig
+++ b/plugins/ScheduledReports/templates/_listReports.twig
@@ -1,5 +1,11 @@
-<div id='entityEditContainer'>
- <table class="dataTable entityTable">
+<div id='entityEditContainer' class="entityTableContainer"
+ piwik-content-block
+ content-title="{{ title|e('html_attr') }}"
+ help-url="http://piwik.org/docs/email-reports/"
+ feature="true"
+ ng-show="manageScheduledReport.showReportsList">
+
+ <table piwik-content-table>
<thead>
<tr>
<th class="first">{{ 'General_Description'|translate }}</th>
@@ -23,6 +29,7 @@
</tr>
{% elseif reports is empty %}
<tr>
+
<td colspan='7'>
<br/>
{{ 'ScheduledReports_ThereIsNoReportToManage'|translate(siteName)|raw }}.
@@ -58,7 +65,9 @@
<br/>
{% endfor %}
{# send now link #}
- <a href="#" idreport="{{ report.idreport }}" name="linkSendNow" class="link_but withIcon" style="margin-top:3px;">
+ <a href="#"
+ ng-click="manageScheduledReport.sendReportNow({{ report.idreport }})"
+ name="linkSendNow" class="link_but withIcon" style="margin-top:3px;">
<img border=0 src='{{ reportTypes[report.type] }}'/>
{{ 'ScheduledReports_SendReportNow'|translate }}
</a>
@@ -76,29 +85,30 @@
{{ 'General_Download'|translate }}
</a>
</td>
- <td class="text-center">
- <button id="{{ report.idreport }}" class="edit-report btn btn-flat btn-lg" title="{{ 'General_Edit'|translate }}">
+ <td style="text-align: center;padding-top:2px;">
+ <button ng-click="manageScheduledReport.editReport({{ report.idreport }})"
+ class="table-action" title="{{ 'General_Edit'|translate|e('html_attr') }}">
<span class="icon-edit"></span>
</button>
</td>
- <td class="text-center">
- <button id="{{ report.idreport }}" class="delete-report btn btn-flat btn-lg" title="{{ 'General_Delete'|translate }}">
+ <td style="text-align: center;padding-top:2px;">
+ <button ng-click="manageScheduledReport.deleteReport({{ report.idreport }})"
+ class="table-action" title="{{ 'General_Delete'|translate|e('html_attr') }}">
<span class="icon-delete"></span>
</button>
</td>
</tr>
{% endfor %}
{% endif %}
-
</table>
- {% if userLogin != 'anonymous' %}
- <p>
- <button id="add-report" class="btn btn-lg btn-flat">
+ <div class="tableActionBar">
+ {% if userLogin != 'anonymous' %}
+ <button id="add-report" ng-click="manageScheduledReport.createReport()" >
<span class="icon-add"></span>
{{ 'ScheduledReports_CreateAndScheduleReport'|translate }}
</button>
- </p>
- {% endif %}
+ {% endif %}
+ </div>
</div>
diff --git a/plugins/ScheduledReports/templates/index.twig b/plugins/ScheduledReports/templates/index.twig
index 05949d6b0a..1ba11502e6 100644
--- a/plugins/ScheduledReports/templates/index.twig
+++ b/plugins/ScheduledReports/templates/index.twig
@@ -9,13 +9,12 @@
{% block content %}
-<div class="emailReports">
- <h2 piwik-enriched-headline help-url="http://piwik.org/docs/email-reports/">{{ title }}</h2>
+<div class="emailReports" piwik-manage-scheduled-report>
<span id="reportSentSuccess"></span>
<span id="reportUpdatedSuccess"></span>
- <div class="entityContainer">
+ <div>
{% import 'ajaxMacros.twig' as ajax %}
{{ ajax.errorDiv() }}
{{ ajax.loadingDiv() }}
@@ -40,9 +39,6 @@
ReportPlugin.reportList = {{ reportsJSON | raw }};
ReportPlugin.createReportString = "{{ 'ScheduledReports_CreateReport'|translate }}";
ReportPlugin.updateReportString = "{{ 'ScheduledReports_UpdateReport'|translate }}";
- $(function () {
- initManagePdf();
- });
</script>
<style type="text/css">
.reportCategory {
diff --git a/plugins/ScheduledReports/templates/reportParametersScheduledReports.twig b/plugins/ScheduledReports/templates/reportParametersScheduledReports.twig
index 95a787ecaf..19acdd9276 100644
--- a/plugins/ScheduledReports/templates/reportParametersScheduledReports.twig
+++ b/plugins/ScheduledReports/templates/reportParametersScheduledReports.twig
@@ -1,78 +1,52 @@
-<tr class='{{ reportType }}'>
- <td style='width:240px;' class="first">{{ 'ScheduledReports_SendReportTo'|translate }}
- </td>
- <td>
- <input type="checkbox" id="report_email_me"/>
- <label for="report_email_me">{{ 'ScheduledReports_SentToMe'|translate }} (<em>{{ currentUserEmail }}</em>) </label>
- <br/><br/>
- {{ 'ScheduledReports_AlsoSendReportToTheseEmails'|translate }}<br/>
- <textarea cols="30" rows="3" id="report_additional_emails" class="inp"></textarea>
- <script>
- function updateEvolutionGraphParameterVisibility() {
- var evolutionGraphParameterInput = $('.report_evolution_graph');
- var nonApplicableDisplayFormats = ['1', '4'];
- $.inArray($('#display_format').find('option:selected').val(), nonApplicableDisplayFormats) != -1 ?
- evolutionGraphParameterInput.hide() : evolutionGraphParameterInput.show();
+<div piwik-field uicontrol="checkbox"
+ name="report_email_me"
+ introduction="{{ 'ScheduledReports_SendReportTo'|translate|e('html_attr') }}"
+ ng-show="manageScheduledReport.report.type == 'email'"
+ ng-model="manageScheduledReport.report.emailMe"
+ title="{{ 'ScheduledReports_SentToMe'|translate|e('html_attr') }} ({{ currentUserEmail|e('html_attr') }})">
+</div>
+
+<div piwik-field uicontrol="textarea" var-type="array"
+ ng-show="manageScheduledReport.report.type == 'email'"
+ ng-model="manageScheduledReport.report.additionalEmails"
+ title="{{ 'ScheduledReports_AlsoSendReportToTheseEmails'|translate|e('html_attr') }}">
+</div>
+
+<script>
+
+ $(function () {
+
+ resetReportParametersFunctions ['{{ reportType }}'] = function (report) {
+ report.displayFormat = '{{ defaultDisplayFormat }}';
+ report.emailMe = {{ defaultEmailMe }};
+ report.evolutionGraph = {{ defaultEvolutionGraph }};
+ report.additionalEmails = '';
+ };
+
+ updateReportParametersFunctions['{{ reportType }}'] = function (report) {
+ if (report == null || report.parameters == null) {
+ return;
}
- $(function () {
-
- resetReportParametersFunctions ['{{ reportType }}'] =
- function () {
-
- var reportParameters = {
- 'displayFormat': '{{ defaultDisplayFormat }}',
- 'emailMe': {{ defaultEmailMe }},
- 'evolutionGraph': {{ defaultEvolutionGraph }},
- 'additionalEmails': null
- };
- updateReportParametersFunctions['{{ reportType }}'](reportParameters);
- };
-
- updateReportParametersFunctions['{{ reportType }}'] =
- function (reportParameters) {
-
- if (reportParameters == null) return;
-
- $('#display_format').find('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('');
-
- $(document).trigger('ScheduledReport.edit', {});
- };
-
- getReportParametersFunctions['{{ reportType }}'] =
- function () {
-
- var parameters = Object();
+ var i, field, fields = ['displayFormat', 'emailMe', 'evolutionGraph', 'additionalEmails'];
+ for (i in fields) {
+ field = fields[i];
+ if (report.parameters[field]) {
+ report[field] = report.parameters[field];
+ }
+ }
+ };
- parameters.displayFormat = $('#display_format').find('option:selected').val();
- parameters.emailMe = $('#report_email_me').prop('checked');
- parameters.evolutionGraph = $('#report_evolution_graph').prop('checked');
+ getReportParametersFunctions['{{ reportType }}'] = function (report) {
- var additionalEmails = $('#report_additional_emails').val();
- parameters.additionalEmails =
- additionalEmails != '' ? additionalEmails.split('\n') : [];
+ var parameters = {};
- return parameters;
- };
+ parameters.displayFormat = report.displayFormat;
+ parameters.emailMe = report.emailMe;
+ parameters.evolutionGraph = report.evolutionGraph;
+ parameters.additionalEmails = report.additionalEmails ? report.additionalEmails : [];
- $('#display_format').change(updateEvolutionGraphParameterVisibility);
- });
- </script>
- </td>
-</tr> \ No newline at end of file
+ return parameters;
+ };
+ });
+</script> \ No newline at end of file
diff --git a/plugins/ScheduledReports/tests/Integration/ApiTest.php b/plugins/ScheduledReports/tests/Integration/ApiTest.php
index e2d242581f..5062ae1569 100644
--- a/plugins/ScheduledReports/tests/Integration/ApiTest.php
+++ b/plugins/ScheduledReports/tests/Integration/ApiTest.php
@@ -358,7 +358,10 @@ class ApiTest extends IntegrationTestCase
$report6['period'] = Schedule::PERIOD_NEVER;
$report6['deleted'] = 0;
- $stubbedAPIScheduledReports = $this->getMock('\\Piwik\\Plugins\\ScheduledReports\\API', array('getReports', 'getInstance'), $arguments = array(), $mockClassName = '', $callOriginalConstructor = false);
+ $stubbedAPIScheduledReports = $this->getMockBuilder('\\Piwik\\Plugins\\ScheduledReports\\API')
+ ->setMethods(array('getReports', 'getInstance'))
+ ->disableOriginalConstructor()
+ ->getMock();
$stubbedAPIScheduledReports->expects($this->any())->method('getReports')->will($this->returnValue(
array($report1, $report2, $report3, $report4, $report5, $report6))
);
@@ -436,7 +439,7 @@ class ApiTest extends IntegrationTestCase
{
$realProxy = new Proxy();
- $mockProxy = $this->getMock('Piwik\API\Proxy', array('call'));
+ $mockProxy = $this->getMockBuilder('Piwik\API\Proxy')->setMethods(array('call'))->getMock();
$mockProxy->expects($this->any())->method('call')->willReturnCallback(function ($className, $methodName, $parametersRequest) use ($realProxy) {
switch ($className) {
case '\Piwik\Plugins\VisitsSummary\API':
diff --git a/plugins/SecurityInfo b/plugins/SecurityInfo
-Subproject 85790278f1c34d2b12c9a4d846df81daee905bb
+Subproject 8288bbf2e03ec5c6a1d0e279262b785d0ec9d14
diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php
index 1e6e4d3ec7..f3ea172a54 100644
--- a/plugins/SegmentEditor/SegmentEditor.php
+++ b/plugins/SegmentEditor/SegmentEditor.php
@@ -90,5 +90,7 @@ class SegmentEditor extends \Piwik\Plugin
$translationKeys[] = 'SegmentEditor_CustomSegment';
$translationKeys[] = 'SegmentEditor_VisibleToSuperUser';
$translationKeys[] = 'SegmentEditor_SharedWithYou';
+ $translationKeys[] = 'SegmentEditor_ChooseASegment';
+ $translationKeys[] = 'SegmentEditor_CurrentlySelectedSegment';
}
}
diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js
index ce3c166cf1..f80f3fa193 100644
--- a/plugins/SegmentEditor/javascripts/Segmentation.js
+++ b/plugins/SegmentEditor/javascripts/Segmentation.js
@@ -71,26 +71,44 @@ Segmentation = (function($) {
this.currentSegmentStr = segmentStr;
};
+ segmentation.prototype.setTooltip = function (segmentDescription) {
+
+ var title = _pk_translate('SegmentEditor_ChooseASegment') + '.';
+ title += ' '+ _pk_translate('SegmentEditor_CurrentlySelectedSegment', [segmentDescription]);
+
+ $(this.content).attr('title', title);
+ }
+
segmentation.prototype.markCurrentSegment = function(){
var current = this.getSegment();
var segmentationTitle = $(this.content).find(".segmentationTitle");
+ var title;
if( current != "")
{
var currentDecoded = piwikHelper.htmlDecode(current);
var selector = 'div.segmentList ul li[data-definition="'+currentDecoded+'"]';
var foundItems = $(selector, this.target);
- if( foundItems.length > 0) {
+ if (foundItems.length === 0) {
+ currentDecoded = piwikHelper.htmlDecode(decodeURIComponent(current));
+ selector = 'div.segmentList ul li[data-definition="'+currentDecoded+'"]';
+ foundItems = $(selector, this.target);
+ }
+
+ if (foundItems.length > 0) {
var idSegment = $(foundItems).first().attr('data-idsegment');
- var title = getSegmentName( getSegmentFromId(idSegment));
+ title = getSegmentName(getSegmentFromId(idSegment));
} else {
title = _pk_translate('SegmentEditor_CustomSegment');
}
segmentationTitle.addClass('segment-clicked').html( title );
+ this.setTooltip(title);
}
else {
- $(this.content).find(".segmentationTitle").text(this.translations['SegmentEditor_DefaultAllVisits']);
+ title = this.translations['SegmentEditor_DefaultAllVisits'];
+ segmentationTitle.text(title);
+ this.setTooltip(title);
}
};
@@ -241,7 +259,9 @@ Segmentation = (function($) {
checkSelected = encodeURIComponent(checkSelected);
}
- if( checkSelected == self.currentSegmentStr){
+ if( checkSelected == self.currentSegmentStr
+ || checkSelected == decodeURIComponent(self.currentSegmentStr)
+ || checkSelected == decodeURIComponent(decodeURIComponent(self.currentSegmentStr))){
injClass = 'class="segmentSelected"';
}
listHtml += '<li data-idsegment="'+segment.idsegment+'" data-definition="'+ (segment.definition).replace(/"/g, '&quot;') +'" '
@@ -505,7 +525,11 @@ Segmentation = (function($) {
var idsegment = $(this).attr("data-idsegment");
segmentDefinition = $(this).data("definition");
- self.setSegment(segmentDefinition);
+ if (!piwikHelper.isAngularRenderingThePage()) {
+ // we update segment on location change success
+ self.setSegment(segmentDefinition);
+ }
+
self.markCurrentSegment();
self.segmentSelectMethod( segmentDefinition );
toggleLoadingMessage(segmentDefinition.length);
@@ -972,9 +996,12 @@ Segmentation = (function($) {
}
// remove any remaining forms
+
self.form = getFormHtml();
self.target.prepend(self.form);
+ piwikHelper.setMarginLeftToBeInViewport(self.form);
+
// if there's enough space to the left & not enough space to the right,
// anchor the form to the right of the selector
if (self.form.width() + self.target.offset().left > $(window).width()
@@ -1158,6 +1185,26 @@ Segmentation = (function($) {
toggleLoadingMessage(segmentIsSet);
};
+ if (piwikHelper.isAngularRenderingThePage()) {
+ angular.element(document).injector().invoke(function ($rootScope, $location) {
+ $rootScope.$on('$locationChangeSuccess', function () {
+ var $search = $location.search();
+
+ var segment = '';
+ if ('undefined' !== typeof $search.segment && null !== $search.segment) {
+ segment = $search.segment
+ }
+
+ segment = decodeURIComponent(segment);
+
+ if (self.getSegment() != segment) {
+ self.setSegment(segment);
+ self.initHtml();
+ }
+ });
+ });
+ }
+
this.initHtml();
bindEvents();
};
@@ -1191,7 +1238,29 @@ $(document).ready(function() {
this.changeSegment = function(segmentDefinition) {
segmentDefinition = cleanupSegmentDefinition(segmentDefinition);
segmentDefinition = encodeURIComponent(segmentDefinition);
- return broadcast.propagateNewPage('segment=' + segmentDefinition, true);
+
+ if (piwikHelper.isAngularRenderingThePage()) {
+
+ angular.element(document).injector().invoke(function ($location, $rootScope) {
+ var $search = $location.search();
+
+ if (segmentDefinition !== $search.segment) {
+ // eg when using back button the date might be actually already changed in the URL and we do not
+ // want to change the URL again
+ $search.segment = segmentDefinition;
+ $location.search($search);
+ setTimeout(function () {
+ try {
+ $rootScope.$apply();
+ } catch (e) {}
+ }, 1);
+ }
+
+ });
+ return false;
+ } else {
+ return broadcast.propagateNewPage('segment=' + segmentDefinition, true);
+ }
};
this.changeSegmentList = function () {};
@@ -1221,7 +1290,6 @@ $(document).ready(function() {
self.props.availableSegments.push(params);
self.rebuild();
- self.impl.setSegment(params.definition);
self.impl.markCurrentSegment();
self.$element.find('a.close').click();
@@ -1230,7 +1298,7 @@ $(document).ready(function() {
self.changeSegmentList(self.props.availableSegments);
}
});
- ajaxHandler.send(true);
+ ajaxHandler.send();
};
var updateSegment = function(params){
@@ -1260,7 +1328,6 @@ $(document).ready(function() {
$.extend( self.props.availableSegments[idx], params);
self.rebuild();
- self.impl.setSegment(params.definition);
self.impl.markCurrentSegment();
self.$element.find('a.close').click();
@@ -1269,7 +1336,7 @@ $(document).ready(function() {
self.changeSegmentList(self.props.availableSegments);
}
});
- ajaxHandler.send(true);
+ ajaxHandler.send();
};
var deleteSegment = function(params){
@@ -1303,22 +1370,39 @@ $(document).ready(function() {
self.$element.find('a.close').click();
self.changeSegment('');
+
$('.ui-dialog-content').dialog('close');
self.changeSegmentList(self.props.availableSegments);
}
});
- ajaxHandler.send(true);
+ ajaxHandler.send();
};
- var segmentFromRequest = encodeURIComponent(self.props.selectedSegment)
- || broadcast.getValueFromHash('segment')
- || broadcast.getValueFromUrl('segment');
- if($.browser.mozilla) {
- segmentFromRequest = decodeURIComponent(segmentFromRequest);
+ function getSegmentFromRequest()
+ {
+ var hashStr = broadcast.getHashFromUrl();
+ var segmentFromRequest;
+
+ if (hashStr && hashStr.indexOf('segment=') !== -1) {
+ // needed in case "segment = ''" in hash but set in query via 'segment=foo==bar'.
+ segmentFromRequest = broadcast.getValueFromHash('segment');
+ } else {
+ segmentFromRequest = broadcast.getValueFromHash('segment')
+ || encodeURIComponent(self.props.selectedSegment)
+ || broadcast.getValueFromUrl('segment');
+ }
+
+ if ($.browser.mozilla) {
+ segmentFromRequest = decodeURIComponent(segmentFromRequest);
+ }
+
+ return segmentFromRequest;
}
+ var segmentFromRequest = getSegmentFromRequest();
+
var userSegmentAccess = (this.props.authorizedToCreateSegments) ? "write" : "read";
this.impl = new Segmentation({
diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less
index afc79b3a9b..c0f97e940a 100644
--- a/plugins/SegmentEditor/stylesheets/segmentation.less
+++ b/plugins/SegmentEditor/stylesheets/segmentation.less
@@ -84,15 +84,14 @@ div.scrollable {
border-radius: 3px;
position: absolute;
left: -1px;
- top: -1px;
+ top: 37px;
}
.segment-element .custom_select_search {
width: 146px;
background: url(plugins/SegmentEditor/images/bg-segment-search.png) 0 10px no-repeat;
padding: 10px 0 0 0;
- margin: 10px 0 10px 15px;
- border-top: 1px solid #dcdacf;
+ margin: 0 0 10px 15px;
position: relative;
height: 32px;
}
@@ -203,7 +202,6 @@ div.scrollable {
.segment-element .segment-content .segment-rows {
padding: 4px;
margin: 0 3px 0 0;
- background: @theme-color-background-base;
border: 1px solid #a9a399;
border-radius: 3px 3px 3px 3px;
position: relative;
@@ -214,7 +212,7 @@ div.scrollable {
.segment-element .segment-content .segment-add-or {
font-size: 14px;
font-weight: bold;
- background: @theme-color-background-base;
+ background: @theme-color-background-contrast;
color: #b9b9b9;
text-align: center;
position: relative;
@@ -257,6 +255,7 @@ div.scrollable {
.segment-element .segment-content .segment-add-or > div {
border: 2px dashed #EFEFEB;
+ background-color: #efefeb;
}
.segment-element .segment-content .segment-row {
@@ -323,12 +322,16 @@ div.scrollable {
border-radius: 5px 0 0 5px;
}
+.segment-element .edit_segment_name {
+ width: 200px;
+}
+
.segment-element .segment-content .segment-and {
display: inline-block;
margin: -1px 0 -1px 6%;
z-index: 1;
position: relative;
- background: @theme-color-background-base;
+ background: @theme-color-background-contrast;
padding: 5px 35px;
color: #4f4f4f;
font-size: 14px;
@@ -427,8 +430,6 @@ div.scrollable {
border: 1px solid #e4e5e4;
margin-right: 10px;
border-radius: 4px;
- color: @theme-color-text-light;
- font-size: 14px;
.segmentListContainer {
line-height: 14px;
@@ -450,7 +451,7 @@ div.scrollable {
.segmentationContainer .submenu {
font-size: 13px;
- min-width: 200px;
+ min-width: 206px;
}
.segmentationContainer .submenu ul {
@@ -460,13 +461,14 @@ div.scrollable {
font-weight: normal;
line-height: 20px;
list-style: none outside none;
- margin-left: 5px;
margin-right: 0;
padding-top: 10px;
}
+
+
.segmentationContainer .submenu ul li {
- padding: 2px 0 1px 6px;
+ padding: 2px 0 1px 0;
margin: 3px 0 0 0;
cursor: pointer;
}
@@ -499,9 +501,11 @@ div.scrollable {
}
.segmentEditorPanel.expanded .segmentationContainer {
- width: 240px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
+ .dropdown-body {
+ width: 240px;
+ }
}
.segmentEditorPanel.expanded ul.submenu {
@@ -516,7 +520,7 @@ div.scrollable {
.segmentEditorPanel.expanded .segmentFilterContainer > input[type="text"] {
font-size: 11px;
- width: 200px;
+ width: 206px;
padding: 0;
border: 1px solid #d0d0d0;
border-width: 1px;
@@ -529,7 +533,7 @@ div.scrollable {
width: 13px;
height: 13px;
right: 23px;
- top: 48px;
+ top: 24px;
cursor: pointer;
}
@@ -542,9 +546,8 @@ div.scrollable {
}
.segmentEditorPanel.expanded .add_new_segment {
- clear: both;
- float: right;
- margin: 12px 9px 10px 0;
+ width: 100%;
+ margin: 16px 0 8px 0;
}
.segmentationContainer > ul.submenu > li {
@@ -668,8 +671,7 @@ a.metric_category {
}
.dropdown-body {
- background-color: #f7f7f7;
- border-top-width: 0px;
+ border-top-width: 0;
display: none;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
@@ -680,11 +682,6 @@ a.metric_category {
border-top-right-radius: 0;
}
-.segmentEditorPanel:hover .dropdown-body {
- border-color: #a9a399;
- background: #f1f0eb
-}
-
.segmentEditorPanel.expanded {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
@@ -720,6 +717,7 @@ a.metric_category {
.segmentEditorPanel .segmentationTitle {
text-overflow: ellipsis;
display: inline-block;
+ vertical-align: top;
max-width: 170px;
}
@@ -727,14 +725,8 @@ a.metric_category {
font-weight: bold;
}
-.segmentEditorPanel a.title {
- padding-bottom: 8px;
-}
-
-.segmentEditorPanel.expanded .segmentationTitle {
- max-width: 180px;
- overflow: visible; /* restore default */
- white-space: normal; /* restore default */
+.segmentEditorPanel.expanded a.title {
+ padding-bottom: 10px;
}
.segment-element .segment-nav a.dropdown,
diff --git a/plugins/SegmentEditor/templates/_segmentSelector.twig b/plugins/SegmentEditor/templates/_segmentSelector.twig
index 8d03b6d5bd..c8ab792fc0 100644
--- a/plugins/SegmentEditor/templates/_segmentSelector.twig
+++ b/plugins/SegmentEditor/templates/_segmentSelector.twig
@@ -3,7 +3,7 @@
<a class="title" tabindex="4"><span class="icon icon-segment"></span><span class="segmentationTitle"></span></a>
<div class="dropdown dropdown-body">
<div class="segmentFilterContainer">
- <input class="segmentFilter" type="text" value="{{ 'General_Search'|translate }}"/>
+ <input class="segmentFilter browser-default" type="text" value="{{ 'General_Search'|translate }}"/>
<span/>
</div>
<ul class="submenu">
@@ -43,7 +43,7 @@
<div class="segment-row-inputs">
<div class="segment-input metricListBlock">
- <select title="{{ 'SegmentEditor_ChooseASegment'|translate }}" class="metricList">
+ <select title="{{ 'SegmentEditor_ChooseASegment'|translate }}" class="metricList browser-default">
{% for category,segmentsInCategory in segmentsByCategory %}
<optgroup label="{{ category }}">
{% for segmentInCategory in segmentsInCategory %}
@@ -54,7 +54,7 @@
</select>
</div>
<div class="segment-input metricMatchBlock">
- <select title="{{ 'General_Matches'|translate }}">
+ <select title="{{ 'General_Matches'|translate }}" class="browser-default">
<option value="==">{{ 'General_OperationEquals'|translate }}</option>
<option value="!=">{{ 'General_OperationNotEquals'|translate }}</option>
<option value="<=">{{ 'General_OperationAtMost'|translate }}</option>
@@ -68,7 +68,7 @@
</select>
</div>
<div class="segment-input metricValueBlock">
- <input type="text" title="{{ 'General_Value'|translate }}">
+ <input type="text" class="browser-default" title="{{ 'General_Value'|translate }}">
</div>
<div class="clear"></div>
</div>
@@ -94,8 +94,14 @@
<div class="segment-nav">
<h4 class="visits">
<span class="icon-segment"></span><span class="available_segments"><strong>
- <select class="available_segments_select"></select>
+ <select class="available_segments_select browser-default"></select>
</strong></span></h4>
+
+ <div class="custom_select_search">
+ <a href="#"></a>
+ <input type="text" aria-haspopup="true" aria-autocomplete="list" role="textbox" autocomplete="off" class="inp ui-autocomplete-input segmentSearch browser-default" value="{{ 'General_Search'|translate }}" length="15">
+ </div>
+
<div class="scrollable">
<ul>
{% for category,segmentsInCategory in segmentsByCategory %}
@@ -120,10 +126,6 @@
{% endfor %}
</ul>
</div>
- <div class="custom_select_search">
- <a href="#"></a>
- <input type="text" aria-haspopup="true" aria-autocomplete="list" role="textbox" autocomplete="off" class="inp ui-autocomplete-input segmentSearch" value="{{ 'General_Search'|translate }}" length="15">
- </div>
</div>
<div class="segment-content">
<div class="segment-top" {% if not isSuperUser %}style="display:none"{% endif %}>
@@ -155,9 +157,9 @@
<div class="segment-footer">
<div piwik-rate-feature title="Segment Editor" style="display:inline-block;float: left;margin-top: 2px;margin-right: 10px;"></div>
<span class="segmentFooterNote">The Segment Editor was <a class='crowdfundingLink' href='http://crowdfunding.piwik.org/custom-segments-editor/' rel='noreferrer' target='_blank'>crowdfunded</a> with the awesome support of 80 companies and Piwik users worldwide!</span>
- <a class="delete" href="#">{{ 'General_Delete'|translate }}</a>
- <a class="close" href="#">{{ 'General_Close'|translate }}</a>
- <button class="saveAndApply">{{ 'SegmentEditor_SaveAndApply'|translate }}</button>
+ <a class="btn-flat delete" href="#">{{ 'General_Delete'|translate }}</a>
+ <a class="btn-flat close" href="#">{{ 'General_Close'|translate }}</a>
+ <button class="btn saveAndApply">{{ 'SegmentEditor_SaveAndApply'|translate }}</button>
</div>
</div>
</div>
@@ -170,7 +172,6 @@
<div class="ui-confirm pleaseChangeBrowserAchivingDisabledSetting">
<h2>{{ 'SegmentEditor_SegmentNotApplied'|translate(nameOfCurrentSegment)|raw }}</h2>
{% set segmentSetting %}{{ 'SegmentEditor_AutoArchivePreProcessed'|translate }}{% endset %}
- <input role="yes" type="button" value="{{ 'General_Ok'|translate }}"/>
<p class="description">
{{ 'SegmentEditor_SegmentNotAppliedMessage'|translate(nameOfCurrentSegment)|raw }}
<br/>
@@ -179,5 +180,7 @@
<br/> <br/> {{ 'SegmentEditor_YouMayChangeSetting'|translate('browser_archiving_disabled_enforce', segmentSetting) }}
{% endif %}
</p>
+
+ <input role="yes" type="button" value="{{ 'General_Ok'|translate }}"/>
</div>
</div> \ No newline at end of file
diff --git a/plugins/SitesManager/Controller.php b/plugins/SitesManager/Controller.php
index 5eb1ecf3ed..f77844af9e 100644
--- a/plugins/SitesManager/Controller.php
+++ b/plugins/SitesManager/Controller.php
@@ -34,26 +34,12 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
return $this->renderTemplate('index');
}
-
- public function getMeasurableTypeSettings()
+
+ public function globalSettings()
{
- $idSite = Common::getRequestVar('idSite', 0, 'int');
- $idType = Common::getRequestVar('idType', '', 'string');
-
- if ($idSite >= 1) {
- Piwik::checkUserHasAdminAccess($idSite);
- } else if ($idSite === 0) {
- Piwik::checkUserHasSomeAdminAccess();
- } else {
- throw new Exception('Invalid idSite parameter. IdSite has to be zero or higher');
- }
-
- $view = new View('@SitesManager/measurable_type_settings');
-
-// $propSettings = new MeasurableSettings($idSite, $idType);
- $view->settings = array();
+ Piwik::checkUserHasSuperUserAccess();
- return $view->render();
+ return $this->renderTemplate('globalSettings');
}
public function getGlobalSettings()
diff --git a/plugins/SitesManager/Menu.php b/plugins/SitesManager/Menu.php
index aac879955b..39cb6eee67 100644
--- a/plugins/SitesManager/Menu.php
+++ b/plugins/SitesManager/Menu.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\SitesManager;
use Piwik\Menu\MenuAdmin;
use Piwik\Piwik;
use Piwik\Measurable\Type\TypeManager;
+use Piwik\Plugins\WebsiteMeasurable;
class Menu extends \Piwik\Plugin\Menu
{
@@ -23,17 +24,18 @@ class Menu extends \Piwik\Plugin\Menu
public function configureAdminMenu(MenuAdmin $menu)
{
+ if (Piwik::hasUserSuperUserAccess()) {
+ $menu->addMeasurableItem('General_Settings', $this->urlForAction('globalSettings'), $order = 11);
+ }
+
if (Piwik::isUserHasSomeAdminAccess()) {
+ $menu->addMeasurableItem('SitesManager_MenuManage', $this->urlForAction('index'), $order = 10);
+
$type = $this->getFirstTypeIfOnlyOneIsInUse();
- $menuName = 'General_Measurables';
if ($type) {
- $menuName = $type->getNamePlural();
+ $menu->rename('CoreAdminHome_MenuMeasurables', $subMenuOriginal = null, $type->getNamePlural(), $subMenuRenamed = null);
}
-
- $menu->addManageItem($menuName,
- $this->urlForAction('index'),
- $order = 10);
}
}
diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php
index ee77874aa2..d1d40072ae 100644
--- a/plugins/SitesManager/SitesManager.php
+++ b/plugins/SitesManager/SitesManager.php
@@ -83,7 +83,6 @@ class SitesManager extends \Piwik\Plugin
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/SitesManager/stylesheets/SitesManager.less";
- $stylesheets[] = "plugins/Morpheus/stylesheets/base.less";
}
/**
diff --git a/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js b/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js
index a88b34b969..bdade4b045 100644
--- a/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js
+++ b/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js
@@ -7,12 +7,19 @@
(function () {
angular.module('piwikApp').controller('SitesManagerSiteController', SitesManagerSiteController);
- SitesManagerSiteController.$inject = ['$scope', '$filter', 'sitesManagerApiHelper', 'sitesManagerTypeModel', 'piwikApi'];
+ SitesManagerSiteController.$inject = ['$scope', '$filter', 'sitesManagerApiHelper', 'sitesManagerTypeModel', 'piwikApi', '$timeout'];
- function SitesManagerSiteController($scope, $filter, sitesManagerApiHelper, sitesManagerTypeModel, piwikApi) {
+ function SitesManagerSiteController($scope, $filter, sitesManagerApiHelper, sitesManagerTypeModel, piwikApi, $timeout) {
var translate = $filter('translate');
+ var updateView = function () {
+ $timeout(function () {
+ $('.editingSite').find('select').material_select();
+ Materialize.updateTextFields();
+ });
+ }
+
var init = function () {
initModel();
@@ -58,6 +65,8 @@
}
$scope.site.removeDialog = {};
+
+ updateView();
};
var editSite = function () {
@@ -71,6 +80,8 @@
}, function () {
$scope.site.isLoading = false;
});
+
+ updateView();
};
var saveSite = function() {
@@ -189,7 +200,7 @@
ajaxHandler.redirectOnSuccess($scope.redirectParams);
ajaxHandler.setLoadingElement();
- ajaxHandler.send(true);
+ ajaxHandler.send();
};
init();
diff --git a/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js b/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js
index 1236243d2a..b217677d10 100644
--- a/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js
+++ b/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js
@@ -7,12 +7,23 @@
(function () {
angular.module('piwikApp').controller('SitesManagerController', SitesManagerController);
- SitesManagerController.$inject = ['$scope', '$filter', 'coreAPI', 'sitesManagerAPI', 'piwikApi', 'sitesManagerAdminSitesModel', 'piwik', 'sitesManagerApiHelper', 'sitesManagerTypeModel'];
+ SitesManagerController.$inject = ['$scope', '$filter', 'coreAPI', 'sitesManagerAPI', 'piwikApi', 'sitesManagerAdminSitesModel', 'piwik', 'sitesManagerApiHelper', 'sitesManagerTypeModel', '$rootScope', '$window'];
- function SitesManagerController($scope, $filter, coreAPI, sitesManagerAPI, piwikApi, adminSites, piwik, sitesManagerApiHelper, sitesManagerTypeModel) {
+ function SitesManagerController($scope, $filter, coreAPI, sitesManagerAPI, piwikApi, adminSites, piwik, sitesManagerApiHelper, sitesManagerTypeModel, $rootScope, $window) {
var translate = $filter('translate');
+ $scope.globalSettings = {};
+
+ $rootScope.$on('$locationChangeSuccess', function () {
+ if (piwik.hasSuperUserAccess) {
+ // as we are not using a router yet...
+ if ($window.location.hash === '#globalSettings' || $window.location.hash === '#/globalSettings') {
+ broadcast.propagateNewPage('action=globalSettings');
+ }
+ }
+ });
+
var init = function () {
$scope.period = piwik.broadcast.getValueFromUrl('period');
@@ -40,14 +51,6 @@
$scope.addNewEntity = addNewEntity;
$scope.saveGlobalSettings = saveGlobalSettings;
$scope.lookupCurrentEditSite = lookupCurrentEditSite;
-
- $scope.closeAddMeasurableDialog = function () {
- // I couldn't figure out another way to close that jquery dialog
- var element = angular.element('[piwik-dialog="$parent.showAddSiteDialog"]');
- if (element.parents('ui-dialog') && element.dialog('isOpen')) {
- element.dialog('close');
- }
- }
};
var initAvailableTypes = function () {
@@ -140,8 +143,8 @@
$scope.timezones.push({
group: timezoneGroup,
- code: code,
- label: label
+ key: code,
+ value: label
});
});
});
@@ -219,7 +222,7 @@
ajaxHandler.withTokenInUrl();
ajaxHandler.redirectOnSuccess($scope.redirectParams);
ajaxHandler.setLoadingElement();
- ajaxHandler.send(true);
+ ajaxHandler.send();
};
var cancelEditSite = function (site) {
diff --git a/plugins/SitesManager/lang/en.json b/plugins/SitesManager/lang/en.json
index a7ea1da8c9..3d17edf803 100644
--- a/plugins/SitesManager/lang/en.json
+++ b/plugins/SitesManager/lang/en.json
@@ -33,6 +33,7 @@
"GlobalListExcludedQueryParameters": "Global list of Query URL parameters to exclude",
"GlobalListExcludedUserAgents": "Global list of user agents to exclude",
"GlobalListExcludedUserAgents_Desc": "If the visitor's user agent string contains any of the strings you specify, the visitor will be excluded from Piwik.",
+ "GlobalSettings": "Global settings",
"GlobalWebsitesSettings": "Global websites settings",
"HelpExcludedIps": "Enter the list of IPs, one per line, that you wish to exclude from being tracked by Piwik. You can use wildcards, eg. %1$s or %2$s",
"JsTrackingTagHelp": "Here is the JavaScript Tracking code to include on all your pages",
@@ -44,6 +45,7 @@
"ListOfQueryParametersToBeExcludedOnAllWebsites": "The Query URLs parameters below will be excluded from URLs on all websites.",
"ListOfQueryParametersToExclude": "Enter the list of URL Query Parameters, one per line, to exclude from the Page URLs reports.",
"MainDescription": "Your Web Analytics reports need Websites! Add, update, delete Websites, and show the JavaScript to insert in your pages.",
+ "MenuManage": "Manage",
"NotAnEcommerceSite": "Not an Ecommerce site",
"NotFound": "No websites found for",
"NoWebsites": "You don't have any website to administrate.",
diff --git a/plugins/SitesManager/stylesheets/SitesManager.less b/plugins/SitesManager/stylesheets/SitesManager.less
index cd3a43f7c8..dc3b4e48b0 100644
--- a/plugins/SitesManager/stylesheets/SitesManager.less
+++ b/plugins/SitesManager/stylesheets/SitesManager.less
@@ -19,7 +19,6 @@
.sitesManagerList {
.site {
- .card;
.title {
font-size: 11px;
text-transform: uppercase;
@@ -29,12 +28,9 @@
}
}
.editingSite {
- background-color: @theme-color-background-tinyContrast;
.editingSiteFooter {
- background-color: @theme-color-background-base;
padding: 15px;
margin: 0 -15px -15px;
- border-top: solid 1px #E5E5E5;
}
}
}
diff --git a/plugins/SitesManager/templates/_displayJavascriptCode.twig b/plugins/SitesManager/templates/_displayJavascriptCode.twig
index 772fcadf89..b523ec28e7 100644
--- a/plugins/SitesManager/templates/_displayJavascriptCode.twig
+++ b/plugins/SitesManager/templates/_displayJavascriptCode.twig
@@ -13,5 +13,5 @@
<p>{{ 'CoreAdminHome_JSTrackingIntro5'|translate('<a rel="noreferrer" target="_blank" href="http://piwik.org/docs/javascript-tracking/">','</a>')|raw }}</p>
- <p>{{ 'Installation_JSTracking_EndNote'|translate('<em>','</em>')|raw }}</p>
+ <p>{{ 'Installation_JSTracking_EndNote'|translate('','')|raw }}</p>
</div> \ No newline at end of file
diff --git a/plugins/SitesManager/templates/global-settings.html b/plugins/SitesManager/templates/global-settings.html
index 04e049f9e2..57d291595c 100644
--- a/plugins/SitesManager/templates/global-settings.html
+++ b/plugins/SitesManager/templates/global-settings.html
@@ -1,85 +1,107 @@
-<div ng-show="hasSuperUserAccess">
+<div ng-controller="SitesManagerController" class="SitesManager">
+<div ng-show="hasSuperUserAccess" piwik-content-block content-title="{{ 'SitesManager_GlobalWebsitesSettings' | translate }}">
- <h2 id="globalSettings" class="secondary">{{ 'SitesManager_GlobalWebsitesSettings' | translate }}</h2>
+ <a name="globalSettings" id="globalSettings"></a>
- <h3>{{ 'SitesManager_GlobalListExcludedIps'|translate }}</h3>
- <p>{{ 'SitesManager_ListOfIpsToBeExcludedOnAllWebsites'|translate }}</p>
- <div class="form-group">
+ <div id="excludedIpsGlobalHelp" class="inline-help-node">
<div ng-include="'plugins/SitesManager/templates/help/excluded-ip-help.html'"></div>
- <div sites-manager-multiline-field field="globalSettings.excludedIpsGlobal" cols="30" rows="3" id="excludedIpsGlobal"></div>
</div>
- <h3>{{ 'SitesManager_GlobalListExcludedQueryParameters'|translate }}</h3>
- <p>{{ 'SitesManager_ListOfQueryParametersToBeExcludedOnAllWebsites'|translate }}</p>
- <div class="form-group">
+ <div id="excludedQueryParametersGlobalHelp" class="inline-help-node">
<div ng-include="'plugins/SitesManager/templates/help/excluded-query-parameters-help.html'"></div>
- <div sites-manager-multiline-field field="globalSettings.excludedQueryParametersGlobal" cols="30" rows="3" id="excludedQueryParametersGlobal"></div>
</div>
- <h3>{{ 'SitesManager_GlobalListExcludedUserAgents'|translate }}</h3>
- <p>{{ 'SitesManager_GlobalListExcludedUserAgents_Desc'|translate }}</p>
- <div class="form-group">
+ <div id="excludedUserAgentsGlobalHelp" class="inline-help-node">
<div ng-include="'plugins/SitesManager/templates/help/excluded-user-agents-help.html'"></div>
- <div sites-manager-multiline-field field="globalSettings.excludedUserAgentsGlobal" cols="30" rows="3" id="excludedUserAgentsGlobal"></div>
</div>
- <div class="form-group">
- <div class="form-help" ng-bind-html="'SitesManager_EnableSiteSpecificUserAgentExclude_Help'|translate:'<a href=\'#excludedUserAgentsGlobal\'>':'</a>'"></div>
- <label class="checkbox">
- <input type="checkbox" id="siteSpecificUserAgentExcludeEnabled" ng-model="globalSettings.siteSpecificUserAgentExcludeEnabled">
- {{ 'SitesManager_EnableSiteSpecificUserAgentExclude'|translate }}
- </label>
+
+ <div id="timezoneHelp" class="inline-help-node">
+ <div ng-include="'plugins/SitesManager/templates/help/timezone-help.html'"></div>
</div>
- <h3>{{ 'SitesManager_KeepURLFragments'|translate }}</h3>
- <p ng-bind-html="'SitesManager_KeepURLFragmentsHelp'|translate:'<em>#</em>':'<em>example.org/index.html#first_section</em>':'<em>example.org/index.html</em>'"></p>
- <div class="form-group">
- <p class="form-help">{{ 'SitesManager_KeepURLFragmentsHelp2'|translate }}</p>
- <label class="checkbox">
- <input type="checkbox" id="keepURLFragmentsGlobal" ng-model="globalSettings.keepURLFragmentsGlobal">
- {{ 'SitesManager_KeepURLFragmentsLong'|translate }}
- </label>
+ <div id="keepURLFragmentsHelp" class="inline-help-node">
+ <div ng-bind-html="'SitesManager_KeepURLFragmentsHelp'|translate:'<em>#</em>':'<em>example.org/index.html#first_section</em>':'<em>example.org/index.html</em>'"></div>
+ <div>{{ 'SitesManager_KeepURLFragmentsHelp2'|translate }}</div>
+ </div>
+
+ <div piwik-field uicontrol="textarea" name="excludedIpsGlobal"
+ var-type="array"
+ ng-model="globalSettings.excludedIpsGlobal"
+ title="{{ 'SitesManager_ListOfIpsToBeExcludedOnAllWebsites'|translate }}"
+ introduction="{{ 'SitesManager_GlobalListExcludedIps'|translate }}"
+ inline-help="#excludedIpsGlobalHelp">
+ </div>
+
+ <div piwik-field uicontrol="textarea" name="excludedQueryParametersGlobal"
+ var-type="array"
+ ng-model="globalSettings.excludedQueryParametersGlobal"
+ title="{{ 'SitesManager_ListOfQueryParametersToBeExcludedOnAllWebsites'|translate }}"
+ introduction="{{ 'SitesManager_GlobalListExcludedQueryParameters'|translate }}"
+ inline-help="#excludedQueryParametersGlobalHelp">
+ </div>
+
+ <div piwik-field uicontrol="textarea" name="excludedUserAgentsGlobal"
+ var-type="array"
+ ng-model="globalSettings.excludedUserAgentsGlobal"
+ title="{{ 'SitesManager_GlobalListExcludedUserAgents_Desc'|translate }}"
+ introduction="{{ 'SitesManager_GlobalListExcludedUserAgents'|translate }}"
+ inline-help="#excludedUserAgentsGlobalHelp">
+ </div>
+
+ <div piwik-field uicontrol="checkbox" name="siteSpecificUserAgentExcludeEnabled"
+ ng-model="globalSettings.siteSpecificUserAgentExcludeEnabled"
+ title="{{ 'SitesManager_EnableSiteSpecificUserAgentExclude'|translate }}"
+ inline-help="{{ 'SitesManager_EnableSiteSpecificUserAgentExclude_Help'|translate:'<a href=\'#excludedUserAgentsGlobal\'>':'</a>' }}">
+ </div>
+
+ <div piwik-field uicontrol="checkbox" name="keepURLFragmentsGlobal"
+ ng-model="globalSettings.keepURLFragmentsGlobal"
+ title="{{ 'SitesManager_KeepURLFragmentsLong'|translate }}"
+ introduction="{{ 'SitesManager_KeepURLFragments'|translate }}"
+ inline-help="#keepURLFragmentsHelp">
</div>
<h3>{{ 'SitesManager_TrackingSiteSearch'|translate }}</h3>
+
<p>{{ 'SitesManager_SiteSearchUse' | translate }}</p>
<div class="alert alert-info">
{{ 'SitesManager_SearchParametersNote'|translate }} {{ 'SitesManager_SearchParametersNote2'|translate }}
</div>
- <div class="form-group">
- <label>{{ 'SitesManager_SearchKeywordLabel' | translate }}</label>
- <div class="form-help">
- {{ 'SitesManager_SearchKeywordParametersDesc' | translate }}
- </div>
- <input ng-list size="15" ng-model="globalSettings.searchKeywordParametersGlobal">
+
+ <div piwik-field uicontrol="text" name="searchKeywordParametersGlobal" var-type="array"
+ ng-model="globalSettings.searchKeywordParametersGlobal"
+ title="{{ 'SitesManager_SearchKeywordLabel'|translate }}"
+ inline-help="{{ 'SitesManager_SearchKeywordParametersDesc' | translate }}">
</div>
+
<div ng-hide="customVariablesActivated" class="alert alert-info">
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).
</div>
- <p ng-show="customVariablesActivated">
- {{ 'Goals_Optional'|translate }} {{ 'SitesManager_SearchCategoryDesc'|translate }}
- </p>
- <div class="form-group" ng-show="customVariablesActivated">
- <label>{{ 'SitesManager_SearchCategoryLabel' | translate }}</label>
- <div class="form-help">
- {{ 'Goals_Optional'|translate }} {{ 'SitesManager_SearchCategoryParametersDesc'|translate }}
- </div>
- <input ng-list size="15" ng-model="globalSettings.searchCategoryParametersGlobal">
+
+ <div piwik-field uicontrol="text" name="searchCategoryParametersGlobal" var-type="array"
+ ng-show="customVariablesActivated"
+ ng-model="globalSettings.searchCategoryParametersGlobal"
+ title="{{ 'SitesManager_SearchCategoryLabel'|translate }}"
+ inline-help="{{ 'Goals_Optional'|translate }} {{ 'SitesManager_SearchCategoryDesc'|translate }} {{ 'SitesManager_SearchCategoryParametersDesc'|translate }}">
</div>
- <h3>{{ 'SitesManager_DefaultTimezoneForNewWebsites'|translate }}</h3>
- <p>{{ 'SitesManager_SelectDefaultTimezone'|translate }}</p>
- <div class="form-group">
- <div ng-include="'plugins/SitesManager/templates/help/timezone-help.html'"></div>
- <select ng-model="globalSettings.defaultTimezone" ng-options="t.code as t.label group by t.group for t in timezones"></select>
+ <div piwik-field uicontrol="select" name="defaultTimezone"
+ ng-model="globalSettings.defaultTimezone"
+ options="timezones"
+ title="{{ 'SitesManager_SelectDefaultTimezone'|translate }}"
+ introduction="{{ 'SitesManager_DefaultTimezoneForNewWebsites'|translate }}"
+ inline-help="#timezoneHelp">
</div>
- <h3>{{ 'SitesManager_DefaultCurrencyForNewWebsites'|translate }}</h3>
- <p>{{ 'SitesManager_SelectDefaultCurrency'|translate }}</p>
- <div class="form-group">
- <div class="form-help">{{ 'SitesManager_CurrencySymbolWillBeUsedForGoals' | translate }}</div>
- <select ng-model="globalSettings.defaultCurrency" ng-options="k as v for (k, v) in currencies"></select>
+ <div piwik-field uicontrol="select" name="defaultCurrency"
+ ng-model="globalSettings.defaultCurrency"
+ options="currencies"
+ title="{{ 'SitesManager_SelectDefaultCurrency'|translate }}"
+ introduction="{{ 'SitesManager_DefaultCurrencyForNewWebsites'|translate }}"
+ inline-help="{{ 'SitesManager_CurrencySymbolWillBeUsedForGoals' | translate }}">
</div>
- <input type="submit" class="submit" ng-click="saveGlobalSettings()" value="{{ 'General_Save'|translate }}"/>
+ <div piwik-save-button onconfirm="saveGlobalSettings()"></div>
+</div></div>
</div>
diff --git a/plugins/SitesManager/templates/globalSettings.twig b/plugins/SitesManager/templates/globalSettings.twig
new file mode 100644
index 0000000000..cf7b13ff10
--- /dev/null
+++ b/plugins/SitesManager/templates/globalSettings.twig
@@ -0,0 +1,9 @@
+{% extends 'admin.twig' %}
+
+{% set title %}{{ 'SitesManager_GlobalWebsitesSettings'|translate }}{% endset %}
+
+{% block content %}
+
+ <div ng-include="'plugins/SitesManager/templates/global-settings.html?cb={{ cacheBuster }}'"></div>
+
+{% endblock %}
diff --git a/plugins/SitesManager/templates/help/excluded-ip-help.html b/plugins/SitesManager/templates/help/excluded-ip-help.html
index b12f9439d8..7bfa421d6f 100644
--- a/plugins/SitesManager/templates/help/excluded-ip-help.html
+++ b/plugins/SitesManager/templates/help/excluded-ip-help.html
@@ -1,4 +1,4 @@
-<div class="form-help">
+<div>
{{ 'SitesManager_HelpExcludedIps' | translate : '1.2.3.*' : '1.2.*.*' }}
<br/><br/>
diff --git a/plugins/SitesManager/templates/help/excluded-query-parameters-help.html b/plugins/SitesManager/templates/help/excluded-query-parameters-help.html
index 9ec52250bd..39fed1cf89 100644
--- a/plugins/SitesManager/templates/help/excluded-query-parameters-help.html
+++ b/plugins/SitesManager/templates/help/excluded-query-parameters-help.html
@@ -1,4 +1,4 @@
-<div class="form-help">
+<div>
{{ 'SitesManager_ListOfQueryParametersToExclude'|translate }}
<br/><br/>
diff --git a/plugins/SitesManager/templates/help/excluded-user-agents-help.html b/plugins/SitesManager/templates/help/excluded-user-agents-help.html
index 080443f2cb..52c49a17cc 100644
--- a/plugins/SitesManager/templates/help/excluded-user-agents-help.html
+++ b/plugins/SitesManager/templates/help/excluded-user-agents-help.html
@@ -1,4 +1,4 @@
-<div class="form-help">
+<div>
{{ 'SitesManager_GlobalExcludedUserAgentHelp1'|translate }}
<br/><br/>
diff --git a/plugins/SitesManager/templates/help/timezone-help.html b/plugins/SitesManager/templates/help/timezone-help.html
index f77ad7863e..393606f147 100644
--- a/plugins/SitesManager/templates/help/timezone-help.html
+++ b/plugins/SitesManager/templates/help/timezone-help.html
@@ -1,4 +1,4 @@
-<div class="form-help">
+<div>
<span ng-switch="timezoneSupportEnabled">
<span ng-switch-default>
{{ 'SitesManager_AdvancedTimezoneSupportNotFound'|translate }}
diff --git a/plugins/SitesManager/templates/index.html b/plugins/SitesManager/templates/index.html
index adedf3e644..42e4d426f9 100644
--- a/plugins/SitesManager/templates/index.html
+++ b/plugins/SitesManager/templates/index.html
@@ -5,5 +5,4 @@
<div ng-include="'plugins/SitesManager/templates/sites-list/add-entity-dialog.html?cb=' + cacheBuster"></div>
<div ng-include="'plugins/SitesManager/templates/sites-list/sites-list.html?cb=' + cacheBuster"></div>
<div class="bottomButtonBar" ng-include="'plugins/SitesManager/templates/sites-list/add-site-link.html?cb=' + cacheBuster"></div>
- <div ng-include="'plugins/SitesManager/templates/global-settings.html?cb=' + cacheBuster"></div>
</div>
diff --git a/plugins/SitesManager/templates/measurable_type_settings.twig b/plugins/SitesManager/templates/measurable_type_settings.twig
deleted file mode 100644
index 7c1bb624f2..0000000000
--- a/plugins/SitesManager/templates/measurable_type_settings.twig
+++ /dev/null
@@ -1,7 +0,0 @@
-{% import 'settingsMacros.twig' as settingsMacro %}
-
-{% for name, setting in settings %}
- <fieldset>
- {{ settingsMacro.singleSetting(setting, loop.index) }}
- </fieldset>
-{% endfor %}
diff --git a/plugins/SitesManager/templates/sites-list/add-entity-dialog.html b/plugins/SitesManager/templates/sites-list/add-entity-dialog.html
index f1a1679684..9c9f6a5244 100644
--- a/plugins/SitesManager/templates/sites-list/add-entity-dialog.html
+++ b/plugins/SitesManager/templates/sites-list/add-entity-dialog.html
@@ -1,16 +1,18 @@
-<div piwik-dialog="$parent.showAddSiteDialog"
- title="{{ 'SitesManager_ChooseMeasurableTypeHeadline'|translate }}">
+<div piwik-dialog="$parent.showAddSiteDialog" class="ui-confirm">
- <div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
- <div class="ui-dialog-buttonset">
+ <h2>{{ 'SitesManager_ChooseMeasurableTypeHeadline'|translate }}</h2>
+
+ <p>
+ <div class="center">
<button type="button"
ng-repeat="type in availableTypes"
title="{{ type.description }}"
- class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"
- ng-click="addSite(type.id);closeAddMeasurableDialog()"
+ class="modal-close btn"
+ style="margin-left: 20px;"
+ ng-click="addSite(type.id);"
aria-disabled="false">
<span class="ui-button-text">{{ type.name }}</span>
</button>
</div>
- </div>
+ </p>
</div> \ No newline at end of file
diff --git a/plugins/SitesManager/templates/sites-list/site-fields.html b/plugins/SitesManager/templates/sites-list/site-fields.html
index 29b74a48b5..df8d6e7a3b 100644
--- a/plugins/SitesManager/templates/sites-list/site-fields.html
+++ b/plugins/SitesManager/templates/sites-list/site-fields.html
@@ -1,8 +1,9 @@
-<div class="site" idsite="{{site.idSite}}" ng-class="{'editingSite': site.editMode==true}">
+<div class="site card hoverable" idsite="{{site.idSite}}" ng-class="{'editingSite': site.editMode==true}">
+<div class="card-content">
<div class="row" ng-if="!site.editMode">
- <div class="col-md-3">
+ <div class="col m3">
<h4>{{ site.name }}</h4>
<ul>
<li><span class="title">{{ 'General_Id'|translate }}:</span> {{ site.idsite }}</li>
@@ -15,7 +16,7 @@
</li>
</ul>
</div>
- <div class="col-md-4">
+ <div class="col m4">
<ul>
<li><span class="title">{{ 'SitesManager_Timezone'|translate }}:</span> {{ site.timezone }}</li>
<li><span class="title">{{ 'SitesManager_Currency'|translate }}:</span> {{ site.currency }}</li>
@@ -27,7 +28,7 @@
</li>
</ul>
</div>
- <div class="col-md-4">
+ <div class="col m4">
<ul>
<li>
<span class="title">{{ 'SitesManager_Urls'|translate }}</span>:
@@ -47,15 +48,15 @@
</li>
</ul>
</div>
- <div class="col-md-1 text-right">
+ <div class="col m1 text-right">
<ul>
<li>
- <button class="btn btn-flat btn-lg" ng-click="editSite()" title="{{ 'General_Edit'|translate }}">
+ <button class="table-action" ng-click="editSite()" title="{{ 'General_Edit'|translate }}">
<span class="icon-edit"></span>
</button>
</li>
<li>
- <button class="btn btn-flat btn-lg" ng-show="site.idsite" ng-click="openDeleteDialog()" title="{{ 'General_Delete'|translate }}">
+ <button class="table-action" ng-show="site.idsite" ng-click="openDeleteDialog()" title="{{ 'General_Delete'|translate }}">
<span class="icon-delete"></span>
</button>
</li>
@@ -66,9 +67,12 @@
<div ng-if="site.editMode">
- <div class="form-group">
- <label>{{ 'General_Name'|translate }}</label>
- <input type="text" ng-model="site.name"/>
+ <div class="form-group row">
+ <div class="col s12 m6 input-field">
+ <input type="text" ng-model="site.name"/>
+ <label>{{ 'General_Name'|translate }}</label>
+ </div>
+ <div class="col s12 m6"></div>
</div>
<div piwik-activity-indicator loading="site.isLoading"></div>
@@ -79,18 +83,24 @@
</div>
</div>
- <div class="form-group">
- <label>{{ 'SitesManager_Currency'|translate }}</label>
- <div class="form-help">
+ <div class="form-group row">
+ <div class="col s12 m6 input-field">
+ <select ng-model="site.currency" ng-options="k as v for (k, v) in currencies"></select>
+ <label>{{ 'SitesManager_Currency'|translate }}</label>
+ </div>
+ <div class="col s12 m6 form-help">
{{ 'SitesManager_CurrencySymbolWillBeUsedForGoals' | translate }}
</div>
- <select ng-model="site.currency" ng-options="k as v for (k, v) in currencies"></select>
</div>
- <div class="form-group">
- <label>{{ 'SitesManager_Timezone'|translate }}</label>
- <div ng-include="'plugins/SitesManager/templates/help/timezone-help.html'"></div>
- <select ng-model="site.timezone" ng-options="t.code as t.label group by t.group for t in timezones"></select>
+ <div class="form-group row">
+ <div class="col s12 m6 input-field">
+ <select ng-model="site.timezone" ng-options="t.key as t.value group by t.group for t in timezones"></select>
+ <label>{{ 'SitesManager_Timezone'|translate }}</label>
+ </div>
+ <div class="col s12 m6 form-help">
+ <div ng-include="'plugins/SitesManager/templates/help/timezone-help.html'"></div>
+ </div>
</div>
<div class="editingSiteFooter">
@@ -99,5 +109,5 @@
</div>
</div>
-
+</div>
</div>
diff --git a/plugins/SitesManager/templates/sites-manager-header.html b/plugins/SitesManager/templates/sites-manager-header.html
index 167b20ed83..4a7a1c08cb 100644
--- a/plugins/SitesManager/templates/sites-manager-header.html
+++ b/plugins/SitesManager/templates/sites-manager-header.html
@@ -1,18 +1,20 @@
-<h2
- ng-show="availableTypes"
- piwik-enriched-headline
- help-url="http://piwik.org/docs/manage-websites/"
- feature-name="{{ 'SitesManager_WebsitesManagement'|translate }}">
- {{ 'SitesManager_XManagement'|translate:(availableTypes.length > 1 ? ('General_Measurables'|translate) : ('SitesManager_Sites'|translate)) }}
-</h2>
+<div piwik-content-intro>
+ <h2
+ ng-show="availableTypes"
+ piwik-enriched-headline
+ help-url="http://piwik.org/docs/manage-websites/"
+ feature-name="{{ 'SitesManager_WebsitesManagement'|translate }}">
+ {{ 'SitesManager_XManagement'|translate:(availableTypes.length > 1 ? ('General_Measurables'|translate) : ('SitesManager_Sites'|translate)) }}
+ </h2>
-<p>
- {{ 'SitesManager_MainDescription'|translate }}
+ <p>
+ {{ 'SitesManager_MainDescription'|translate }}
- <span ng-bind-html="'SitesManager_YouCurrentlyHaveAccessToNWebsites'|translate:'<strong>' + totalNumberOfSites + '</strong>'"></span>
+ <span ng-bind-html="'SitesManager_YouCurrentlyHaveAccessToNWebsites'|translate:'<strong>' + totalNumberOfSites + '</strong>'"></span>
- <span ng-show="hasSuperUserAccess">
- <br/>
- <span ng-bind-html="'SitesManager_SuperUserAccessCan'|translate:'<a href=\'#globalSettings\'>':'</a>'"></span>
- </span>
-</p>
+ <span ng-show="hasSuperUserAccess">
+ <br/>
+ <span ng-bind-html="'SitesManager_SuperUserAccessCan'|translate:'<a href=\'#globalSettings\'>':'</a>'"></span>
+ </span>
+ </p>
+</div> \ No newline at end of file
diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml
index 6be613478e..e530794c52 100644
--- a/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml
+++ b/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml
@@ -2,6 +2,7 @@
<result>
<row>
<pluginName>WebsiteMeasurable</pluginName>
+ <title>WebsiteMeasurable</title>
<settings>
<row>
<name>urls</name>
@@ -210,6 +211,7 @@
</row>
<row>
<pluginName>ExampleSettingsPlugin</pluginName>
+ <title>ExampleSettingsPlugin</title>
<settings>
<row>
<name>contact_email</name>
diff --git a/plugins/TasksTimetable b/plugins/TasksTimetable
-Subproject 8212ef0033dd2b3731fccd39c498f61d6ca8147
+Subproject db23b9d9ce2d5e9d9cf1d80c4a80ab8128c2358
diff --git a/plugins/TestRunner/Commands/TestsRunUI.php b/plugins/TestRunner/Commands/TestsRunUI.php
index 3a9a06c080..f227e74ef6 100644
--- a/plugins/TestRunner/Commands/TestsRunUI.php
+++ b/plugins/TestRunner/Commands/TestsRunUI.php
@@ -37,6 +37,7 @@ class TestsRunUI extends ConsoleCommand
$this->addOption('skip-delete-assets', null, InputOption::VALUE_NONE, "Skip deleting of merged assets (will speed up a test run, but not by a lot).");
$this->addOption('screenshot-repo', null, InputOption::VALUE_NONE, "For tests");
$this->addOption('store-in-ui-tests-repo', null, InputOption::VALUE_NONE, "For tests");
+ $this->addOption('debug', null, InputOption::VALUE_NONE, "Enable phantomjs debugging");
$this->addOption('extra-options', null, InputOption::VALUE_REQUIRED, "Extra options to pass to phantomjs.");
}
@@ -54,6 +55,7 @@ class TestsRunUI extends ConsoleCommand
$extraOptions = $input->getOption('extra-options');
$storeInUiTestsRepo = $input->getOption('store-in-ui-tests-repo');
$screenshotRepo = $input->getOption('screenshot-repo');
+ $debug = $input->getOption('debug');
if (!$skipDeleteAssets) {
AssetManager::getInstance()->removeMergedAssets();
@@ -62,6 +64,7 @@ class TestsRunUI extends ConsoleCommand
$this->writeJsConfig();
$options = array();
+ $phantomJsOptions = array();
if ($persistFixtureData) {
$options[] = "--persist-fixture-data";
}
@@ -98,15 +101,20 @@ class TestsRunUI extends ConsoleCommand
$options[] = "--screenshot-repo";
}
+ if ($debug) {
+ $phantomJsOptions[] = "--debug=true";
+ }
+
if ($extraOptions) {
$options[] = $extraOptions;
}
$options = implode(" ", $options);
+ $phantomJsOptions = implode(" ", $phantomJsOptions);
$specs = implode(" ", $specs);
- $cmd = "phantomjs '" . PIWIK_INCLUDE_PATH . "/tests/lib/screenshot-testing/run-tests.js' $options $specs";
+ $cmd = "phantomjs " . $phantomJsOptions . " '" . PIWIK_INCLUDE_PATH . "/tests/lib/screenshot-testing/run-tests.js' $options $specs";
$output->writeln('Executing command: <info>' . $cmd . '</info>');
$output->writeln('');
diff --git a/plugins/TreemapVisualization b/plugins/TreemapVisualization
-Subproject a74d9a9b29d257a00b7cb1b51f70997e45ee23c
+Subproject d27210763471b61730d111cfafb4b87d168c5b1
diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php
index 0ed417e421..a089c60c28 100644
--- a/plugins/UserCountry/Controller.php
+++ b/plugins/UserCountry/Controller.php
@@ -48,6 +48,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$view->thisIP = IP::getIpFromHeader();
$geoIPDatabasesInstalled = GeoIp::isDatabaseInstalled();
$view->geoIPDatabasesInstalled = $geoIPDatabasesInstalled;
+ $view->updatePeriodOptions = $this->getPeriodUpdateOptions();
// check if there is a working provider (that isn't the default one)
$isThereWorkingProvider = false;
@@ -141,11 +142,21 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
private function getGeoIpUpdaterManageScreen()
{
$view = new View('@UserCountry/getGeoIpUpdaterManageScreen');
+
+ $view->updatePeriodOptions = $this->getPeriodUpdateOptions();
$view->geoIPDatabasesInstalled = true;
$this->setUpdaterManageVars($view);
return $view->render();
}
+ private function getPeriodUpdateOptions()
+ {
+ return array(
+ 'month' => Piwik::translate('Intl_PeriodMonth'),
+ 'week' => Piwik::translate('Intl_PeriodWeek')
+ );
+ }
+
/**
* Sets some variables needed by the _updaterManage.twig template.
*
@@ -164,7 +175,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$lastRunTime = GeoIPAutoUpdater::getLastRunTime();
if ($lastRunTime !== false) {
- $view->lastTimeUpdaterRun = '<strong><em>' . $lastRunTime->toString() . '</em></strong>';
+ $view->lastTimeUpdaterRun = '<strong>' . $lastRunTime->toString() . '</strong>';
}
$view->nextRunTime = GeoIPAutoUpdater::getNextRunTime();
diff --git a/plugins/UserCountry/LocationProvider/DefaultProvider.php b/plugins/UserCountry/LocationProvider/DefaultProvider.php
index ab93545dd9..665f7c0e17 100755
--- a/plugins/UserCountry/LocationProvider/DefaultProvider.php
+++ b/plugins/UserCountry/LocationProvider/DefaultProvider.php
@@ -103,10 +103,10 @@ class DefaultProvider extends LocationProvider
{
$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" rel="noreferrer" target="_blank">'
+ array('<strong>', '', '', '</strong>'))
+ . '<p><a href="http://piwik.org/faq/how-to/#faq_163" rel="noreferrer" target="_blank">'
. Piwik::translate('UserCountry_HowToInstallGeoIPDatabases')
- . '</em></a></p>';
+ . '</a></p>';
return array('id' => self::ID, 'title' => self::TITLE, 'description' => $desc, 'order' => 1);
}
}
diff --git a/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php b/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
index b1d97f08fe..a462bd2419 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
@@ -217,11 +217,9 @@ class Pecl extends GeoIp
{
$desc = Piwik::translate('UserCountry_GeoIpLocationProviderDesc_Pecl1') . '<br/><br/>'
. Piwik::translate('UserCountry_GeoIpLocationProviderDesc_Pecl2');
- $installDocs = '<em>'
- . '<a rel="noreferrer" target="_blank" href="http://piwik.org/faq/how-to/#faq_164">'
+ $installDocs = '<a rel="noreferrer" target="_blank" href="http://piwik.org/faq/how-to/#faq_164">'
. Piwik::translate('UserCountry_HowToInstallGeoIpPecl')
- . '</a>'
- . '</em>';
+ . '</a>';
$extraMessage = false;
if ($this->isAvailable()) {
@@ -249,10 +247,10 @@ class Pecl extends GeoIp
$availableDatabaseTypes[] = Piwik::translate('UserCountry_Organization');
}
- $extraMessage .= '<br/><br/>' . Piwik::translate('UserCountry_GeoIPImplHasAccessTo') . ':&nbsp;<strong><em>'
- . implode(', ', $availableDatabaseTypes) . '</em></strong>.';
+ $extraMessage .= '<br/><br/>' . Piwik::translate('UserCountry_GeoIPImplHasAccessTo') . ':&nbsp;<strong>'
+ . implode(', ', $availableDatabaseTypes) . '</strong>.';
- $extraMessage = '<strong><em>' . Piwik::translate('General_Note') . ':&nbsp;</em></strong>' . $extraMessage;
+ $extraMessage = '<strong>' . Piwik::translate('General_Note') . ':&nbsp;</strong>' . $extraMessage;
}
return array('id' => self::ID,
diff --git a/plugins/UserCountry/LocationProvider/GeoIp/Php.php b/plugins/UserCountry/LocationProvider/GeoIp/Php.php
index ce7cec8741..02399ecf8f 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp/Php.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp/Php.php
@@ -323,10 +323,10 @@ class Php extends GeoIp
{
$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 rel="noreferrer" target="_blank" href="http://piwik.org/faq/how-to/#faq_163">'
+ array('<strong>', '</strong>', '<strong>', '</strong>'));
+ $installDocs = '<a rel="noreferrer" target="_blank" href="http://piwik.org/faq/how-to/#faq_163">'
. Piwik::translate('UserCountry_HowToInstallGeoIPDatabases')
- . '</em></a>';
+ . '</a>';
$availableDatabaseTypes = array();
if (self::getPathToGeoIpDatabase(array('GeoIPCity.dat', 'GeoLiteCity.dat')) !== false) {
@@ -345,9 +345,9 @@ class Php extends GeoIp
$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>.';
+ $extraMessage = '<strong>' . Piwik::translate('General_Note') . '</strong>:&nbsp;'
+ . Piwik::translate('UserCountry_GeoIPImplHasAccessTo') . ':&nbsp;<strong>'
+ . implode(', ', $availableDatabaseTypes) . '</strong>.';
return array('id' => self::ID,
'title' => self::TITLE,
diff --git a/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php b/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
index 90a73b8c79..71199f2f0b 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
@@ -169,9 +169,9 @@ class ServerBased extends GeoIp
return Piwik::translate('General_Note') . ':&nbsp;' . Piwik::translate('UserCountry_AssumingNonApache');
}
- $message = "<strong><em>" . Piwik::translate('General_Note') . ':&nbsp;'
+ $message = "<strong>" . Piwik::translate('General_Note') . ':&nbsp;'
. Piwik::translate('UserCountry_FoundApacheModules')
- . "</em></strong>:<br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
+ . "</strong>:<br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
foreach (apache_get_modules() as $name) {
$message .= "<li>$name</li>\n";
}
@@ -218,17 +218,17 @@ class ServerBased extends GeoIp
$title = sprintf(self::TITLE, $serverDesc);
$desc = Piwik::translate('UserCountry_GeoIpLocationProviderDesc_ServerBased1', array('<strong>', '</strong>'))
. '<br/><br/>'
- . '<em>' . Piwik::translate('UserCountry_GeoIpLocationProviderDesc_ServerBasedAnonWarn') . '</em>'
+ . Piwik::translate('UserCountry_GeoIpLocationProviderDesc_ServerBasedAnonWarn')
. '<br/><br/>'
. Piwik::translate('UserCountry_GeoIpLocationProviderDesc_ServerBased2',
- array('<strong><em>', '</em></strong>', '<strong><em>', '</em></strong>'));
+ array('<strong>', '</strong>', '<strong>', '</strong>'));
$installDocs =
- '<em><a rel="noreferrer" target="_blank" href="http://piwik.org/faq/how-to/#faq_165">'
+ '<a rel="noreferrer" target="_blank" href="http://piwik.org/faq/how-to/#faq_165">'
. Piwik::translate('UserCountry_HowToInstallApacheModule')
- . '</a></em><br/><em>'
+ . '</a><br/>'
. '<a rel="noreferrer" target="_blank" href="http://piwik.org/faq/how-to/#faq_166">'
. Piwik::translate('UserCountry_HowToInstallNginxModule')
- . '</a></em>';
+ . '</a>';
$geoipServerVars = array();
foreach ($_SERVER as $key => $value) {
@@ -238,10 +238,10 @@ class ServerBased extends GeoIp
}
if (empty($geoipServerVars)) {
- $extraMessage = '<strong><em>' . Piwik::translate('UserCountry_GeoIPNoServerVars', '$_SERVER') . '</em></strong>';
+ $extraMessage = '<strong>' . Piwik::translate('UserCountry_GeoIPNoServerVars', '$_SERVER') . '</strong>';
} else {
- $extraMessage = '<strong><em>' . Piwik::translate('UserCountry_GeoIPServerVarsFound', '$_SERVER')
- . ":</em></strong><br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
+ $extraMessage = '<strong>' . Piwik::translate('UserCountry_GeoIPServerVarsFound', '$_SERVER')
+ . ":</strong><br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
foreach ($geoipServerVars as $key) {
$extraMessage .= '<li>' . $key . "</li>\n";
}
diff --git a/plugins/UserCountry/Menu.php b/plugins/UserCountry/Menu.php
index 0bdc6602ac..3a4788e101 100644
--- a/plugins/UserCountry/Menu.php
+++ b/plugins/UserCountry/Menu.php
@@ -16,7 +16,7 @@ class Menu extends \Piwik\Plugin\Menu
public function configureAdminMenu(MenuAdmin $menu)
{
if (UserCountry::isGeoLocationAdminEnabled() && Piwik::hasUserSuperUserAccess()) {
- $menu->addManageItem('UserCountry_Geolocation',
+ $menu->addSystemItem('UserCountry_Geolocation',
$this->urlForAction('adminIndex'),
$order = 30);
}
diff --git a/plugins/UserCountry/UserCountry.php b/plugins/UserCountry/UserCountry.php
index fd616321ff..0e1fa71728 100644
--- a/plugins/UserCountry/UserCountry.php
+++ b/plugins/UserCountry/UserCountry.php
@@ -76,6 +76,10 @@ class UserCountry extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/UserCountry/javascripts/userCountry.js";
+ $jsFiles[] = "plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.controller.js";
+ $jsFiles[] = "plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.directive.js";
+ $jsFiles[] = "plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.controller.js";
+ $jsFiles[] = "plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.directive.js";
}
/**
@@ -118,6 +122,8 @@ class UserCountry extends \Piwik\Plugin
$translationKeys[] = "UserCountry_FatalErrorDuringDownload";
$translationKeys[] = "UserCountry_SetupAutomaticUpdatesOfGeoIP";
$translationKeys[] = "General_Done";
+ $translationKeys[] = "General_Save";
+ $translationKeys[] = "General_Continue";
}
public static function isGeoLocationAdminEnabled()
diff --git a/plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.controller.js b/plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.controller.js
new file mode 100644
index 0000000000..1c431d76e8
--- /dev/null
+++ b/plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.controller.js
@@ -0,0 +1,73 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('LocationProviderSelectionController', LocationProviderSelectionController);
+
+ LocationProviderSelectionController.$inject = ['piwikApi'];
+
+ function LocationProviderSelectionController(piwikApi) {
+ var self = this;
+
+ this.isLoading = false;
+ this.updateLoading = {};
+
+ // handle 'refresh location' link click
+ this.refreshProviderInfo = function (providerId) {
+
+ this.updateLoading[providerId] = true;
+
+ // this should not be in a controller... ideally we fetch this data always from client side and do not
+ // prefill it server side
+ var $locationNode = $('.provider' + providerId + ' .location');
+ $locationNode.css('visibility', 'hidden');
+
+ piwikApi.fetch({
+ module: 'UserCountry',
+ action: 'getLocationUsingProvider',
+ id: providerId,
+ format: 'html'
+ }).then(function (response) {
+ self.updateLoading[providerId] = false;
+ $locationNode.html('<strong>' + response + '</strong>').css('visibility', 'visible');
+ }, function () {
+ self.updateLoading[providerId] = false;
+ });
+ };
+
+ this.save = function () {
+ if (!this.selectedProvider) {
+ return;
+ }
+
+ this.isLoading = true;
+
+ var parent = $(this).closest('p'),
+ loading = $('.loadingPiwik', parent),
+ ajaxSuccess = $('.success', parent);
+
+ piwikApi.withTokenInUrl();
+ piwikApi.fetch({
+ module: 'UserCountry',
+ action: 'setCurrentLocationProvider',
+ id: this.selectedProvider
+ }).then(function () {
+ self.isLoading = false;
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('General_Done'), {
+ context: 'success',
+ noclear: true,
+ type: 'toast',
+ id: 'userCountryLocationProvider'
+ });
+ notification.scrollToNotification();
+ }, function () {
+ self.isLoading = false;
+ });
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.directive.js b/plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.directive.js
new file mode 100644
index 0000000000..b0c5e1c886
--- /dev/null
+++ b/plugins/UserCountry/angularjs/location-provider-selection/location-provider-selection.directive.js
@@ -0,0 +1,33 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-location-provider-selection>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikLocationProviderSelection', piwikLocationProviderSelection);
+
+ piwikLocationProviderSelection.$inject = ['piwik'];
+
+ function piwikLocationProviderSelection(piwik){
+
+ return {
+ restrict: 'A',
+ transclude: true,
+ controller: 'LocationProviderSelectionController',
+ controllerAs: 'locationSelector',
+ template: '<div ng-transclude></div>',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, controller) {
+ controller.selectedProvider = attrs.piwikLocationProviderSelection;
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.controller.js b/plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.controller.js
new file mode 100644
index 0000000000..8c94467ec1
--- /dev/null
+++ b/plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.controller.js
@@ -0,0 +1,149 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('LocationProviderUpdaterController', LocationProviderUpdaterController);
+
+ LocationProviderUpdaterController.$inject = ['piwikApi'];
+
+ function LocationProviderUpdaterController(piwikApi) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+ var self = this;
+
+ this.buttonUpdateSaveText = _pk_translate('General_Save');
+ this.progressUpdateLabel = '';
+
+ // geoip database wizard
+ var downloadNextChunk = function (action, thisId, progressBarId, cont, extraData, callback) {
+ var data = {};
+ for (var k in extraData) {
+ data[k] = extraData[k];
+ }
+
+ piwikApi.withTokenInUrl();
+ piwikApi.post({
+ module: 'UserCountry',
+ action: action,
+ 'continue': cont ? 1 : 0
+ }, data).then(function (response) {
+ if (!response || response.error) {
+ callback(response);
+ } else {
+ // update progress bar
+ var newProgressVal = Math.ceil((response.current_size / response.expected_file_size) * 100);
+ self[progressBarId] = Math.min(newProgressVal, 100);
+
+ // if incomplete, download next chunk, otherwise, show updater manager
+ if (newProgressVal < 100) {
+ downloadNextChunk(action, thisId, progressBarId, true, extraData, callback);
+ } else {
+ callback(response);
+ }
+ }
+ }, function () {
+ callback({error: _pk_translate('UserCountry_FatalErrorDuringDownload')});
+ });
+ };
+
+ this.startDownloadFreeGeoIp = function () {
+ this.showFreeDownload = true;
+ this.showPiwikNotManagingInfo = false;
+
+ this.progressFreeDownload = 0;
+
+ // start download of free dbs
+ downloadNextChunk(
+ 'downloadFreeGeoIPDB',
+ 'geoipdb-screen2-download',
+ 'progressFreeDownload',
+ false,
+ {},
+ function (response) {
+ if (response.error) {
+ $('#geoipdb-update-info').html(response.error);
+ self.geoipDatabaseInstalled = true;
+ } else {
+ self.showGeoIpUpdateInfo();
+ }
+ }
+ );
+ };
+
+ this.startAutomaticUpdateGeoIp = function () {
+ this.buttonUpdateSaveText = _pk_translate('General_Continue');
+ this.showGeoIpUpdateInfo();
+ };
+
+ this.showGeoIpUpdateInfo = function () {
+ this.geoipDatabaseInstalled = true;
+
+ // todo we need to replace this the proper way eventually
+ $('#geoip-db-mangement .card-title').text(_pk_translate('UserCountry_SetupAutomaticUpdatesOfGeoIP'));
+ }
+
+ this.saveGeoIpLinks = function () {
+ var currentDownloading = null;
+ var updateGeoIPSuccess = function (response) {
+ if (response && response.error) {
+ self.isUpdatingGeoIpDatabase = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(response.error, {
+ placeat: '#geoipdb-update-info-error',
+ context: 'error',
+ style: {display: 'inline-block'},
+ id: 'userCountryGeoIpUpdate'
+ });
+
+ } else if (response && response.to_download) {
+ var continuing = currentDownloading == response.to_download;
+ currentDownloading = response.to_download;
+
+ // show progress bar w/ message
+ self.progressUpdateDownload = 0;
+ self.progressUpdateLabel = response.to_download_label;
+ self.isUpdatingGeoIpDatabase = true;
+
+ // start/continue download
+ downloadNextChunk(
+ 'downloadMissingGeoIpDb', 'geoipdb-update-info', 'progressUpdateDownload',
+ continuing, {key: response.to_download}, updateGeoIPSuccess);
+
+ } else {
+ self.progressUpdateLabel = '';
+ self.isUpdatingGeoIpDatabase = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('General_Done'), {
+ placeat: '#done-updating-updater',
+ context: 'success',
+ noclear: true,
+ type: 'toast',
+ style: {display: 'inline-block'},
+ id: 'userCountryGeoIpUpdate'
+ });
+
+ $('#geoip-updater-next-run-time').html(response.nextRunTime).parent().effect('highlight', {color: '#FFFFCB'}, 2000);
+ }
+ };
+
+ piwikApi.withTokenInUrl();
+ piwikApi.post({
+ period: this.updatePeriod,
+ module: 'UserCountry',
+ action: 'updateGeoIPLinks',
+ }, {
+ loc_db: this.locationDbUrl,
+ isp_db: this.ispDbUrl,
+ org_db: this.orgDbUrl
+ }).then(updateGeoIPSuccess);
+ };
+
+
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.directive.js b/plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.directive.js
new file mode 100644
index 0000000000..441689f714
--- /dev/null
+++ b/plugins/UserCountry/angularjs/location-provider-updater/location-provider-updater.directive.js
@@ -0,0 +1,37 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-location-provider-selection>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikLocationProviderUpdater', piwikLocationProviderUpdater);
+
+ piwikLocationProviderUpdater.$inject = ['piwik'];
+
+ function piwikLocationProviderUpdater(piwik){
+
+ return {
+ restrict: 'A',
+ transclude: true,
+ controller: 'LocationProviderUpdaterController',
+ controllerAs: 'locationUpdater',
+ template: '<div ng-transclude></div>',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, controller) {
+ controller.geoipDatabaseInstalled = '0' !== attrs.geoipDatabaseInstalled;
+ controller.showFreeDownload = false;
+ controller.showPiwikNotManagingInfo = true;
+ controller.progressFreeDownload = 0;
+ controller.progressUpdateDownload = 0;
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UserCountry/javascripts/userCountry.js b/plugins/UserCountry/javascripts/userCountry.js
index 68a74adb7a..bbdb5e32b9 100755
--- a/plugins/UserCountry/javascripts/userCountry.js
+++ b/plugins/UserCountry/javascripts/userCountry.js
@@ -6,209 +6,5 @@
*/
$(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).closest('p'),
- loading = $('.loadingPiwik', parent),
- ajaxSuccess = $('.success', parent);
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.setLoadingElement(loading);
- ajaxRequest.addParams({
- module: 'UserCountry',
- action: 'setCurrentLocationProvider',
- id: $(this).val()
- }, 'get');
- ajaxRequest.withTokenInUrl();
- ajaxRequest.setCallback(
- function () {
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
- notification.show(_pk_translate('General_Done'), {
- placeat: ajaxSuccess,
- context: 'success',
- noclear: true,
- type: 'toast',
- style: {display:'inline-block', marginTop: '10px'},
- id: 'userCountryLocationProvider'
- });
- }
- );
- 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');
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.setLoadingElement(loading);
- ajaxRequest.addParams({
- module: 'UserCountry',
- action: 'getLocationUsingProvider',
- id: $(this).attr('data-impl-id')
- }, 'get');
- ajaxRequest.setCallback(
- function (response) {
- location.html('<strong><em>' + response + '</em></strong>').css('visibility', 'visible');
- }
- );
- 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,
- 'continue': cont ? 1 : 0
- };
- for (var k in extraData) {
- data[k] = extraData[k];
- }
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(data, 'post');
- ajaxRequest.withTokenInUrl();
- 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')});
- });
- 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
- $('#geoipdb-screen2-download').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'));
- $('#geoipdb-update-info').fadeIn(1000);
- });
- })
- .on('click', '#update-geoip-links', function () {
- var currentDownloading = null;
- var updateGeoIPSuccess = function (response) {
- if (response && response.error) {
- $('#geoip-progressbar-container').hide();
-
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
- notification.show(response.error, {
- placeat: '#geoipdb-update-info-error',
- context: 'error',
- style: {display: 'inline-block'},
- id: 'userCountryGeoIpUpdate'
- });
-
- }
- 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 {
- $('#geoip-updater-progressbar-label').html('');
- $('#geoip-progressbar-container').hide();
-
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
- notification.show(_pk_translate('General_Done'), {
- placeat: '#done-updating-updater',
- context: 'success',
- noclear: true,
- type: 'toast',
- style: {display: 'inline-block'},
- id: 'userCountryGeoIpUpdate'
- });
-
- $('#geoip-updater-next-run-time').html(response.nextRunTime).parent().effect('highlight', {color: '#FFFFCB'}, 2000);
- }
- };
-
- // setup the auto-updater
- var ajaxRequest = new ajaxHelper();
- var periodSelected = $('#geoip-update-period-cell').find('input:checked').val();
- ajaxRequest.addParams({
- period: periodSelected
- }, '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()
- }, 'post');
- ajaxRequest.withTokenInUrl();
- ajaxRequest.setCallback(updateGeoIPSuccess);
- ajaxRequest.send(false);
- }
- );
+
});
diff --git a/plugins/UserCountry/stylesheets/userCountry.less b/plugins/UserCountry/stylesheets/userCountry.less
index d91c8bc0b1..fc080b25c3 100755
--- a/plugins/UserCountry/stylesheets/userCountry.less
+++ b/plugins/UserCountry/stylesheets/userCountry.less
@@ -1,60 +1,28 @@
-.locationProviderTable label {
- font-size: 1.2em;
-}
input.location-provider {
cursor: pointer;
}
span.is-installed {
- color: green;
+ color: #009874;
}
span.is-broken {
- color: red;
+ color: #D4291F;
}
.loc-provider-status {
margin-left: .5em;
}
-#manage-geoip-dbs {
- height: 20em;
-}
-
-#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-screen1>div {
- display: inline-block;
- vertical-align: top;
-}
-
#geoipdb-screen1>div>p {
- font-size: 2em;
- height: 4em;
-}
-
-.geoipdb-column-1, .geoipdb-column-2 {
- width: 396px;
-}
-
-.geoipdb-column-1 {
- margin-right: 50px;
-}
-
-.geoipdb-column-2 {
- border-left: 1px solid #999;
- padding-left: 50px;
-}
-
-.geoipdb-column-1>p {
- padding-left: 20px;
+ line-height: 25px;
+ font-size: 18px;
+ max-width: 400px;
}
.error {
@@ -63,12 +31,6 @@ span.is-broken {
padding: 4px 8px 4px 8px;
}
-#geoip-updater-progressbar-label {
- float: left;
- margin: -24px 24px;
-}
-
-#geoip-progressbar-container, #geoipdb-update-info-error {
- margin: 22px 24px;
- display: inline-block;
+#done-updating-updater {
+ margin-top: 16px;
} \ No newline at end of file
diff --git a/plugins/UserCountry/templates/_updaterManage.twig b/plugins/UserCountry/templates/_updaterManage.twig
index 4f614787f1..edb8258de2 100755
--- a/plugins/UserCountry/templates/_updaterManage.twig
+++ b/plugins/UserCountry/templates/_updaterManage.twig
@@ -1,68 +1,68 @@
-<div id="geoipdb-update-info" {% if not geoIPDatabasesInstalled %}style="display:none;"{% endif %}>
- <p>{{ 'UserCountry_GeoIPUpdaterInstructions'|translate('<a href="http://www.maxmind.com/en/download_files?rId=piwik" _target="blank">','</a>',
+<div ng-show="locationUpdater.geoipDatabaseInstalled" id="geoipdb-update-info">
+ <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>')|raw }}
<br/><br/>
-{{ 'UserCountry_GeoLiteCityLink'|translate("<a href='"~geoLiteUrl~"'>",geoLiteUrl,'</a>')|raw }}
- {% if geoIPDatabasesInstalled %}
- <br/><br/>{{ 'UserCountry_GeoIPUpdaterIntro'|translate }}:
- {% endif %}
- </p>
- <table class="adminTable" style="width:900px;">
- <tr>
- <th>{{ 'Live_GoalType'|translate }}</th>
- <th>{{ 'Actions_ColumnDownloadURL'|translate }}</th>
- <th></th>
- </tr>
- <tr>
- <td width="150">{{ 'UserCountry_LocationDatabase'|translate }}</td>
- <td><input type="text" id="geoip-location-db" value="{{ geoIPLocUrl }}"/></td>
- <td width="164">
- <div class="form-help">
- {{ 'UserCountry_LocationDatabaseHint'|translate }}
- </div>
- </td>
- </tr>
- <tr>
- <td width="150">{{ 'UserCountry_ISPDatabase'|translate }}</td>
- <td><input type="text" id="geoip-isp-db" value="{{ geoIPIspUrl }}"/></td>
- </tr>
- <tr>
- <td width="150">{{ 'UserCountry_OrgDatabase'|translate }}</td>
- <td><input type="text" id="geoip-org-db" value="{{ geoIPOrgUrl }}"/></td>
- </tr>
- <tr>
- <td width="150">{{ 'UserCountry_DownloadNewDatabasesEvery'|translate }}</td>
- <td id="geoip-update-period-cell">
- <input type="radio" name="geoip-update-period" value="month" id="geoip-update-period-month" {% if geoIPUpdatePeriod == 'month' %}checked="checked"{% endif %} />
- <label for="geoip-update-period-month">{{ 'Intl_PeriodMonth'|translate }}</label>
+ {{ 'UserCountry_GeoLiteCityLink'|translate("<a href='"~geoLiteUrl~"'>",geoLiteUrl,'</a>')|raw }}
- <input type="radio" name="geoip-update-period" value="week" id="geoip-update-period-week" {% if geoIPUpdatePeriod == 'week' %}checked="checked"{% endif %}/>
- <label for="geoip-update-period-week">{{ 'Intl_PeriodWeek'|translate }}</label>
- </td>
- <td width="164">
- <div class="form-help">
- {% if lastTimeUpdaterRun is defined and lastTimeUpdaterRun is not empty %}
- {{ 'UserCountry_UpdaterWasLastRun'|translate(lastTimeUpdaterRun)|raw }}
- {% else %}
- {{ 'UserCountry_UpdaterHasNotBeenRun'|translate }}
- {% endif %}
- <br/><br/>
- <div id="geoip-updater-next-run-time">
- {% include "@UserCountry/_updaterNextRunTime.twig" %}
- </div>
- </div>
- </td>
- </tr>
- </table>
- <p style="display:inline-block;vertical-align:top;">
- <input type="button" class="submit" value="{% if not geoIPDatabasesInstalled %}{{ 'General_Continue'|translate }}{% else %}{{ 'General_Save'|translate }}{% endif %}" id="update-geoip-links"/>
+ <span ng-show="locationUpdater.geoipDatabaseInstalled">
+ <br/><br/>{{ 'UserCountry_GeoIPUpdaterIntro'|translate }}:
+ </span>
</p>
- <div style="display:inline-block;width:700px;">
+
+ <div piwik-field uicontrol="text" name="geoip-location-db"
+ ng-model="locationUpdater.locationDbUrl"
+ introduction="{{ 'UserCountry_LocationDatabase'|translate|e('html_attr') }}"
+ title="{{ 'Actions_ColumnDownloadURL'|translate|e('html_attr') }}"
+ value="{{ geoIPLocUrl }}"
+ inline-help="{{ 'UserCountry_LocationDatabaseHint'|translate|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="text" name="geoip-isp-db"
+ ng-model="locationUpdater.ispDbUrl"
+ introduction="{{ 'UserCountry_ISPDatabase'|translate|e('html_attr') }}"
+ title="{{ 'Actions_ColumnDownloadURL'|translate|e('html_attr') }}"
+ value="{{ geoIPIspUrl }}">
+ </div>
+
+ <div piwik-field uicontrol="text" name="geoip-org-db"
+ ng-model="locationUpdater.orgDbUrl"
+ introduction="{{ 'UserCountry_OrgDatabase'|translate|e('html_attr') }}"
+ title="{{ 'Actions_ColumnDownloadURL'|translate|e('html_attr') }}"
+ value="{{ geoIPOrgUrl }}">
+ </div>
+
+ <div id="locationProviderUpdatePeriodInlineHelp" class="inline-help-node">
+ {% if lastTimeUpdaterRun is defined and lastTimeUpdaterRun is not empty %}
+ {{ 'UserCountry_UpdaterWasLastRun'|translate(lastTimeUpdaterRun)|raw }}
+ {% else %}
+ {{ 'UserCountry_UpdaterHasNotBeenRun'|translate }}
+ {% endif %}
+ <br/><br/>
+ <div id="geoip-updater-next-run-time">
+ {% include "@UserCountry/_updaterNextRunTime.twig" %}
+ </div>
+ </div>
+
+ <div piwik-field uicontrol="radio" name="geoip-update-period"
+ ng-model="locationUpdater.updatePeriod"
+ introduction="{{ 'UserCountry_DownloadNewDatabasesEvery'|translate|e('html_attr') }}"
+ value="{{ geoIPUpdatePeriod }}"
+ options="{{ updatePeriodOptions|json_encode }}"
+ inline-help="#locationProviderUpdatePeriodInlineHelp">
+ </div>
+
+ <input type="button"
+ class="btn"
+ ng-click="locationUpdater.saveGeoIpLinks()"
+ ng-value="locationUpdater.buttonUpdateSaveText"/>
+
+ <div>
<div id="done-updating-updater"></div>
<div id="geoipdb-update-info-error"></div>
- <div id="geoip-progressbar-container" style="display:none;">
- <div id="geoip-updater-progressbar"></div>
- <span id="geoip-updater-progressbar-label"></span>
- </div>
+ <div piwik-progressbar
+ progress="locationUpdater.progressUpdateDownload"
+ label="locationUpdater.progressUpdateLabel"
+ ng-show="locationUpdater.isUpdatingGeoIpDatabase"></div>
</div>
</div>
diff --git a/plugins/UserCountry/templates/_updaterNextRunTime.twig b/plugins/UserCountry/templates/_updaterNextRunTime.twig
index 514de7b1f1..69882f08b4 100644
--- a/plugins/UserCountry/templates/_updaterNextRunTime.twig
+++ b/plugins/UserCountry/templates/_updaterNextRunTime.twig
@@ -2,7 +2,7 @@
{% if date(nextRunTime.getTimestamp()) <= date() %}
{{ 'UserCountry_UpdaterScheduledForNextRun'|translate }}
{% else %}
- {{ 'UserCountry_UpdaterWillRunNext'|translate('<strong><em>' ~ nextRunTime.toString() ~ '</em></strong>')|raw }}
+ {{ 'UserCountry_UpdaterWillRunNext'|translate('<strong>' ~ nextRunTime.toString() ~ '</strong>')|raw }}
{% endif %}
{% else %}
{{ 'UserCountry_UpdaterIsNotScheduledToRun'|translate }}
diff --git a/plugins/UserCountry/templates/adminIndex.twig b/plugins/UserCountry/templates/adminIndex.twig
index c9b41dca97..7fec097d1d 100755
--- a/plugins/UserCountry/templates/adminIndex.twig
+++ b/plugins/UserCountry/templates/adminIndex.twig
@@ -5,128 +5,145 @@
{% block content %}
{% import 'macros.twig' as piwik %}
-<h2 piwik-enriched-headline
- help-url="http://piwik.org/docs/geo-locate/"
- id="location-providers">{{ title }}</h2>
-
-<div style="width:900px;">
-
+<div piwik-content-intro>
+ <h2 piwik-enriched-headline
+ help-url="http://piwik.org/docs/geo-locate/"
+ id="location-providers">{{ title }}</h2>
<p>{{ 'UserCountry_GeolocationPageDesc'|translate }}</p>
+</div>
+<div piwik-content-block content-title="{{ 'UserCountry_LocationProvider'|translate|e('html_attr') }}">
+<div piwik-location-provider-selection="{{ currentProviderId|e('html_attr') }}">
{% if not 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 rel="noreferrer" target="_blank" href="http://www.maxmind.com/?rId=piwik">','</a>')|raw }}</li>
- <li>{{ 'UserCountry_HowToSetupGeoIP_Step2'|translate("'GeoLiteCity.dat'",'<strong>','</strong>')|raw }}</li>
- <li>{{ 'UserCountry_HowToSetupGeoIP_Step3'|translate('<strong>','</strong>','<span style="color:green"><strong>','</strong></span>')|raw }}</li>
- <li>{{ 'UserCountry_HowToSetupGeoIP_Step4'|translate }}</li>
+ <ul style="list-style:disc !important;margin-left:2em;">
+ <li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step1'|translate('<a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz">','</a>','<a rel="noreferrer" target="_blank" href="http://www.maxmind.com/?rId=piwik">','</a>')|raw }}</li>
+ <li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step2'|translate("'GeoLiteCity.dat'",'<strong>','</strong>')|raw }}</li>
+ <li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step3'|translate('<strong>','</strong>','<span style="color:green"><strong>','</strong></span>')|raw }}</li>
+ <li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step4'|translate }}</li>
</ul>
<p>&nbsp;</p>
{% endif %}
- <table class="adminTable locationProviderTable">
- <tr>
- <th>{{ 'UserCountry_LocationProvider'|translate }}</th>
- <th>{{ 'General_Description'|translate }}</th>
- <th>{{ 'General_InfoFor'|translate(thisIP) }}</th>
- </tr>
- {% for id,provider in locationProviders %}
- <tr>
- <td width="150">
- <p>
- <input class="location-provider" name="location-provider" value="{{ id }}" type="radio" {% if currentProviderId == id %}checked="checked"{% endif %}
- id="provider_input_{{ id }}" {% if provider.status != 1 %}disabled="disabled"{% endif %}/>
- <label for="provider_input_{{ id }}">{{ provider.title|translate }}</label><br/>
- <span class="loadingPiwik" style="display:none;"><img src="./plugins/Morpheus/images/loading-blue.gif"/></span>
- <span class="success" ></span>
- </p>
+ <div class="row">
+ <div class="col s12 push-m9 m3">{{ 'General_InfoFor'|translate(thisIP) }}</div>
+ </div>
- <p class="loc-provider-status">
- <strong><em>
- {% if provider.status == 0 %}
- <span class="is-not-installed">{{ 'General_NotInstalled'|translate}}</span>
- {% elseif provider.status == 1 %}
- <span class="is-installed">{{ 'General_Installed'|translate }}</span>
- {% elseif provider.status == 2 %}
- <span class="is-broken">{{ 'General_Broken'|translate }}</span>
- {% endif %}
- </em></strong>
- </p>
- </td>
- <td>
- <p>{{ provider.description|translate|raw }}</p>
- {% if provider.status != 1 and provider.install_docs is defined %}
- <p>{{ provider.install_docs|raw }}</p>
- {% endif %}
- </td>
- <td width="164">
- {% if provider.status == 1 %}
- <div class="form-help">
- {% if thisIP != '127.0.0.1' %}
- {{ 'UserCountry_CurrentLocationIntro'|translate }}:
- <div>
- <br/>
- <span class="loadingPiwik" style="display:none;position:absolute;">
- <img src="./plugins/Morpheus/images/loading-blue.gif"/> {{ 'General_Loading'|translate }}</span>
- <span class="location"><strong>{{ provider.location|raw }}</strong></span>
- </div>
- <div class="text-right">
- <a href="#" class="refresh-loc" data-impl-id="{{ id }}"><em>{{ 'General_Refresh'|translate }}</em></a>
- </div>
- {% else %}
- {{ 'UserCountry_CannotLocalizeLocalIP'|translate(thisIP) }}
- {% endif %}
- </div>
- {% endif %}
- {% if provider.statusMessage is defined and provider.statusMessage %}
- <div class="form-help">
- {% if provider.status == 2 %}<strong>{{ 'General_Error'|translate }}:</strong> {% endif %}{{ provider.statusMessage|raw }}
- </div>
+ {% for id,provider in locationProviders %}
+ <div class="row form-group provider{{ id|e('html_attr') }}">
+ <div class="col s12 m4 l2">
+ <p>
+ <input class="location-provider"
+ name="location-provider"
+ value="{{ id }}"
+ type="radio"
+ ng-model="locationSelector.selectedProvider"
+ id="provider_input_{{ id }}" {% if provider.status != 1 %}disabled="disabled"{% endif %}/>
+ <label for="provider_input_{{ id }}">{{ provider.title|translate }}</label>
+ </p>
+ <p class="loc-provider-status">
+ {% if provider.status == 0 %}
+ <span class="is-not-installed">{{ 'General_NotInstalled'|translate}}</span>
+ {% elseif provider.status == 1 %}
+ <span class="is-installed">{{ 'General_Installed'|translate }}</span>
+ {% elseif provider.status == 2 %}
+ <span class="is-broken">{{ 'General_Broken'|translate }}</span>
{% endif %}
- {% if provider.extra_message is defined and provider.extra_message %}
- <div class="form-help">
- {{ provider.extra_message|raw }}
- </div>
- {% endif %}
- </td>
- {% endfor %}
- </table>
+ </p>
+ </div>
+ <div class="col s12 m4 l6">
+ <p>{{ provider.description|translate|raw }}</p>
+ {% if provider.status != 1 and provider.install_docs is defined %}
+ <p>{{ provider.install_docs|raw }}</p>
+ {% endif %}
+ </div>
+ <div class="col s12 m4 l4">
+ {% if provider.status == 1 %}
+ <div class="form-help">
+ {% if thisIP != '127.0.0.1' %}
+ {{ 'UserCountry_CurrentLocationIntro'|translate }}:
+ <div>
+ <br/>
+ <div style="position: absolute;"
+ piwik-activity-indicator
+ loading='locationSelector.updateLoading[{{ id|json_encode }}]'></div>
+ <span class="location"><strong>{{ provider.location|raw }}</strong></span>
+ </div>
+ <div class="text-right">
+ <a href="javascript:;"
+ ng-click='locationSelector.refreshProviderInfo({{ id|json_encode }})'>{{ 'General_Refresh'|translate }}</a>
+ </div>
+ {% else %}
+ {{ 'UserCountry_CannotLocalizeLocalIP'|translate(thisIP) }}
+ {% endif %}
+ </div>
+ {% endif %}
+ {% if provider.statusMessage is defined and provider.statusMessage %}
+ <div class="form-help">
+ {% if provider.status == 2 %}<strong>{{ 'General_Error'|translate }}:</strong> {% endif %}{{ provider.statusMessage|raw }}
+ </div>
+ {% endif %}
+ {% if provider.extra_message is defined and provider.extra_message %}
+ <div class="form-help">
+ {{ provider.extra_message|raw }}
+ </div>
+ {% endif %}
+ </div>
+ </div>
+ {% endfor %}
-</div>
+ <div piwik-save-button onconfirm="locationSelector.save()" saving="locationSelector.isLoading"></div>
-{% if not geoIPDatabasesInstalled %}
- <h2 id="geoip-db-mangement">{{ 'UserCountry_GeoIPDatabases'|translate }}</h2>
-{% else %}
- <h2 id="geoip-db-mangement">{{ 'UserCountry_SetupAutomaticUpdatesOfGeoIP'|translate }}</h2>
-{% endif %}
+</div>
+</div>
+<div piwik-content-block
+ content-title="{% if not geoIPDatabasesInstalled %}{{ 'UserCountry_GeoIPDatabases'|translate|e('html_attr') }}{% else %}{{ 'UserCountry_SetupAutomaticUpdatesOfGeoIP'|translate|e('html_attr') }}{% endif %}"
+ id="geoip-db-mangement">
-{% if showGeoIPUpdateSection %}
- <div id="manage-geoip-dbs" style="width:900px;" class="adminTable">
+ <div piwik-location-provider-updater
+ geoip-database-installed="{% if geoIPDatabasesInstalled %}1{% else %}0{% endif %}">
- {% if not geoIPDatabasesInstalled %}
- <div id="geoipdb-screen1">
- <p>{{ 'UserCountry_PiwikNotManagingGeoIPDBs'|translate }}</p>
+ {% if showGeoIPUpdateSection %}
+ {% if not geoIPDatabasesInstalled %}
+ <div ng-show="!locationUpdater.geoipDatabaseInstalled">
+ <div ng-show="locationUpdater.showPiwikNotManagingInfo">
+ <h3>{{ 'UserCountry_PiwikNotManagingGeoIPDBs'|translate|e('html_attr') }}</h3>
+ <div id="manage-geoip-dbs">
+ <div class="row" id="geoipdb-screen1">
+ <div class="geoipdb-column-1 col s6">
+ <p>{{ 'UserCountry_IWantToDownloadFreeGeoIP'|translate|raw }}</p>
+ </div>
+ <div class="geoipdb-column-2 col s6">
+ <p>{{ 'UserCountry_IPurchasedGeoIPDBs'|translate('<a href="http://www.maxmind.com/en/geolocation_landing?rId=piwik">','</a>')|raw }}</p>
+ </div>
+ <div class="geoipdb-column-1 col s6">
+ <input type="button" class="btn"
+ ng-click="locationUpdater.startDownloadFreeGeoIp()"
+ value="{{ 'General_GetStarted'|translate }}..."/>
+ </div>
+ <div class="geoipdb-column-2 col s6">
+ <input type="button" class="btn"
+ ng-click="locationUpdater.startAutomaticUpdateGeoIp()"
+ value="{{ 'General_GetStarted'|translate }}..." id="start-automatic-update-geoip"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="geoipdb-screen2-download" ng-show="locationUpdater.showFreeDownload">
+ <div piwik-progressbar
+ label="{{ ('UserCountry_DownloadingDb'|translate('<a href="'~geoLiteUrl~'">GeoLiteCity.dat</a>') ~ '...')|json_encode }}"
+ progress="locationUpdater.progressFreeDownload">
+ </div>
+ </div>
+ </div>
+ {% endif %}
- <div class="geoipdb-column-1">
- <p>{{ 'UserCountry_IWantToDownloadFreeGeoIP'|translate|raw }}</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>')|raw }}</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='./plugins/Morpheus/images/loading-blue.gif'/>
- {{ 'UserCountry_DownloadingDb'|translate('<a href="'~geoLiteUrl~'">GeoLiteCity.dat</a>')|raw }}...</p>
- <div id="geoip-download-progress"></div>
- </div>
- {% endif %}
- {% include "@UserCountry/_updaterManage.twig" %}
-{% else %}
-<p style="width:900px;" class="form-description">{{ 'UserCountry_CannotSetupGeoIPAutoUpdating'|translate }}</p>
-{% endif %}
+ {% include "@UserCountry/_updaterManage.twig" %}
+ {% else %}
+ <p class="form-description">{{ 'UserCountry_CannotSetupGeoIPAutoUpdating'|translate }}</p>
+ {% endif %}
+ </div>
</div>
{% endblock %}
diff --git a/plugins/UserCountry/templates/getDistinctCountries.twig b/plugins/UserCountry/templates/getDistinctCountries.twig
index ce78d5ca68..de8a81db7d 100644
--- a/plugins/UserCountry/templates/getDistinctCountries.twig
+++ b/plugins/UserCountry/templates/getDistinctCountries.twig
@@ -1,5 +1,7 @@
-<div class="sparkline">
- {{ sparkline(urlSparklineCountries) }}
- {{ 'UserCountry_DistinctCountries'|translate("<strong>"~numberDistinctCountries|number~"</strong>")|raw }}
-</div>
-<br style="clear:left"/> \ No newline at end of file
+<div piwik-content-block>
+ <div class="sparkline">
+ {{ sparkline(urlSparklineCountries) }}
+ {{ 'UserCountry_DistinctCountries'|translate("<strong>"~numberDistinctCountries|number~"</strong>")|raw }}
+ </div>
+ <br style="clear:left"/>
+</div> \ No newline at end of file
diff --git a/plugins/UserCountryMap/javascripts/realtime-map.js b/plugins/UserCountryMap/javascripts/realtime-map.js
index 5724294276..c116709ff7 100644
--- a/plugins/UserCountryMap/javascripts/realtime-map.js
+++ b/plugins/UserCountryMap/javascripts/realtime-map.js
@@ -59,13 +59,15 @@
_initStandaloneMap: function () {
$('#periodString').hide();
initTopControls();
- $('#secondNavBar').on('piwikSwitchPage', function (event, item) {
- var href = $(item).attr('href');
+
+ var $rootScope = piwikHelper.getAngularDependency('$rootScope');
+ $rootScope.$on('piwikPageChange', function () {
+ var href = location.href;
var clickedMenuIsNotMap = !href || (href.indexOf('module=UserCountryMap&action=realtimeWorldMap') == -1);
if (clickedMenuIsNotMap) {
$('#periodString').show();
initTopControls();
- }
+ };
});
$('.realTimeMap_overlay').css('top', '0px');
$('.realTimeMap_datetime').css('top', '20px');
@@ -220,7 +222,7 @@
// User ID
(r.userId ? _pk_translate('General_UserId') + ':&nbsp;' + r.userId + '<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 ? '' + ad[ad.length - 1].pageTitle + '<br/>' : '') +
// time of visit
'<div class="rel-time" data-actiontime="' + r.lastActionTimestamp + '">' + relativeTime(ds) + '</div>' +
// either from or direct
@@ -632,8 +634,8 @@
map.symbolGroups[0].update();
}
- if (w < 355) $('.tableIcon span').hide();
- else $('.tableIcon span').show();
+ if (w < 355) $('.UserCountryMap .tableIcon span').hide();
+ else $('.UserCountryMap .tableIcon span').show();
},
_destroy: function () {
diff --git a/plugins/UserCountryMap/javascripts/visitor-map.js b/plugins/UserCountryMap/javascripts/visitor-map.js
index 9597152095..b6a1a24d51 100644
--- a/plugins/UserCountryMap/javascripts/visitor-map.js
+++ b/plugins/UserCountryMap/javascripts/visitor-map.js
@@ -310,7 +310,7 @@
$$('.UserCountryMap_map').off('click').click(zoomOut);
// handle window resizes
- $(window).off('resize').resize(onResizeLazy);
+ $(window).off('resize', onResizeLazy).resize(onResizeLazy);
// enable metric changes
$$('.userCountryMapSelectMetrics').off('change').change(function () {
@@ -1271,8 +1271,8 @@
map.container.height(h - 2);
map.resize(w, h);
- if (w < 355) $('.tableIcon span').hide();
- else $('.tableIcon span').show();
+ if (w < 355) $('.UserCountryMap .tableIcon span').hide();
+ else $('.UserCountryMap .tableIcon span').show();
},
/*
diff --git a/plugins/UserCountryMap/stylesheets/realtime-map.less b/plugins/UserCountryMap/stylesheets/realtime-map.less
index ac5c5533ff..c199ed3cd4 100644
--- a/plugins/UserCountryMap/stylesheets/realtime-map.less
+++ b/plugins/UserCountryMap/stylesheets/realtime-map.less
@@ -27,6 +27,10 @@
background: #D5D3C8;
}
+.card .RealTimeMap_container {
+ position: relative;
+}
+
.RealTimeMap .loadingPiwik {
position: absolute !important;
top: 42% !important;
diff --git a/plugins/UserCountryMap/stylesheets/visitor-map.less b/plugins/UserCountryMap/stylesheets/visitor-map.less
index 223a9a4c89..f6cd3fd555 100644
--- a/plugins/UserCountryMap/stylesheets/visitor-map.less
+++ b/plugins/UserCountryMap/stylesheets/visitor-map.less
@@ -232,7 +232,6 @@
margin-bottom: 5px;
max-width: 10em;
font-size: 10px;
- .native-select();
}
.ui-tooltip.qtip {
diff --git a/plugins/UserCountryMap/templates/realtimeMap.twig b/plugins/UserCountryMap/templates/realtimeMap.twig
index e44ed78a38..84de8d95e4 100644
--- a/plugins/UserCountryMap/templates/realtimeMap.twig
+++ b/plugins/UserCountryMap/templates/realtimeMap.twig
@@ -1,4 +1,4 @@
-<div class="RealTimeMap" style="position:relative; overflow:hidden;"
+<div class="card"><div class="RealTimeMap card-content" style="position:relative; overflow:hidden;"
data-standalone="{{ mapIsStandaloneNotWidget|default(0) }}"
data-config="{{ config|json_encode }}"
tabindex="0">
@@ -26,4 +26,5 @@
</div>
</div>
+</div>
<script type="text/javascript">UserCountryMap.RealtimeMap.initElements();</script> \ No newline at end of file
diff --git a/plugins/UserCountryMap/templates/visitorMap.twig b/plugins/UserCountryMap/templates/visitorMap.twig
index b5445a2cc6..9940efbdaf 100644
--- a/plugins/UserCountryMap/templates/visitorMap.twig
+++ b/plugins/UserCountryMap/templates/visitorMap.twig
@@ -1,5 +1,5 @@
-<section>
-<div class="UserCountryMap" style="position:relative; overflow:hidden;">
+<section class="card">
+<div class="UserCountryMap card-content" style="position:relative; overflow:hidden;">
<div class="UserCountryMap_container">
<div class="UserCountryMap_map" style="overflow:hidden;"></div>
<div class="UserCountryMap-overlay UserCountryMap-title">
@@ -55,12 +55,12 @@
</div>
</div>
- <select class="userCountryMapSelectMetrics" style="float:right;margin-right:0;margin-bottom:5px;max-width: 10em;font-size:10px;">
+ <select class="userCountryMapSelectMetrics browser-default" style="float:right;margin-right:10px;margin-bottom:10px;max-width: 10em;font-size:10px;height: auto;">
{% for metric in metrics %}
<option value="{{ metric[0] }}" {% if metric[0] == defaultMetric %}selected="selected"{% endif %}}>{{ metric[1] }}</option>
{% endfor %}
</select>
- <select class="userCountryMapSelectCountry">
+ <select class="userCountryMapSelectCountry browser-default" style="height: auto;">
<option value="world">{{ 'UserCountryMap_WorldWide'|translate }}</option>
<option disabled="disabled">––––––</option>
{% for code,continent in continents %}
diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php
index b0bb0a88f0..921d7a035c 100644
--- a/plugins/UsersManager/Controller.php
+++ b/plugins/UsersManager/Controller.php
@@ -255,24 +255,45 @@ class Controller extends ControllerAdmin
if ($defaultReport == 'MultiSites') {
$defaultSiteId = $userPreferences->getDefaultWebsiteId();
+ $reportOptionsValue = $defaultSiteId;
$view->defaultReportIdSite = $defaultSiteId;
$view->defaultReportSiteName = Site::getNameFor($defaultSiteId);
} else {
+ $reportOptionsValue = $defaultReport;
$view->defaultReportIdSite = $defaultReport;
$view->defaultReportSiteName = Site::getNameFor($defaultReport);
}
+ $view->defaultReportOptions = array(
+ array('key' => 'MultiSites', 'value' => Piwik::translate('General_AllWebsitesDashboard')),
+ array('key' => $reportOptionsValue, 'value' => Piwik::translate('General_DashboardForASpecificWebsite')),
+ );
+
$view->defaultDate = $this->getDefaultDateForUser($userLogin);
$view->availableDefaultDates = $this->getDefaultDates();
- $view->languages = APILanguagesManager::getInstance()->getAvailableLanguageNames();
+ $languages = APILanguagesManager::getInstance()->getAvailableLanguageNames();
+ $languageOptions = array();
+ foreach ($languages as $language) {
+ $languageOptions[] = array(
+ 'key' => $language['code'],
+ 'value' => $language['name']
+ );
+ }
+
+ $view->languageOptions = $languageOptions;
$view->currentLanguageCode = LanguagesManager::getLanguageCodeForCurrentUser();
- $view->currentTimeformat = LanguagesManager::uses12HourClockForCurrentUser();
+ $view->currentTimeformat = (int) LanguagesManager::uses12HourClockForCurrentUser();
$view->ignoreCookieSet = IgnoreCookie::isIgnoreCookieFound();
$view->piwikHost = Url::getCurrentHost();
$this->setBasicVariablesView($view);
+ $view->timeFormats = array(
+ '1' => Piwik::translate('General_12HourClock'),
+ '0' => Piwik::translate('General_24HourClock')
+ );
+
return $view->render();
}
@@ -316,23 +337,28 @@ class Controller extends ControllerAdmin
if (!Piwik::hasUserSuperUserAccess()) {
return;
}
+
$userLogin = 'anonymous';
// Which websites are available to the anonymous users?
$anonymousSitesAccess = Request::processRequest('UsersManager.getSitesAccessFromUser', array('userLogin' => $userLogin));
$anonymousSites = array();
+ $idSites = array();
foreach ($anonymousSitesAccess as $info) {
$idSite = $info['site'];
+ $idSites[] = $idSite;
$site = Request::processRequest('SitesManager.getSiteFromId', array('idSite' => $idSite));
// Work around manual website deletion
if (!empty($site)) {
- $anonymousSites[$idSite] = $site;
+ $anonymousSites[] = array('key' => $idSite, 'value' => $site['name']);
}
}
$view->anonymousSites = $anonymousSites;
+ $anonymousDefaultSite = '';
+
// Which report is displayed by default to the anonymous user?
$anonymousDefaultReport = Request::processRequest('UsersManager.getUserPreference', array('userLogin' => $userLogin, 'preferenceName' => APIUsersManager::PREFERENCE_DEFAULT_REPORT));
if ($anonymousDefaultReport === false) {
@@ -342,13 +368,29 @@ class Controller extends ControllerAdmin
// 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);
+ $anonymousDefaultReport = '1';
+ $anonymousDefaultSite = $anonymousSites[0]['key'];
}
}
- $view->anonymousDefaultReport = $anonymousDefaultReport;
+ if (is_numeric($anonymousDefaultReport)) {
+ $anonymousDefaultSite = $anonymousDefaultReport;
+ $anonymousDefaultReport = '1'; // a website is selected, we make sure "Dashboard for a specific site" gets pre-selected
+ }
+
+ if ((empty($anonymousDefaultSite) || !in_array($anonymousDefaultSite, $idSites)) && !empty($idSites)) {
+ $anonymousDefaultSite = $anonymousSites[0]['key'];
+ }
+
+ $view->anonymousDefaultReport = $anonymousDefaultReport;
+ $view->anonymousDefaultSite = $anonymousDefaultSite;
$view->anonymousDefaultDate = $this->getDefaultDateForUser($userLogin);
+
+ $view->defaultReportOptions = array(
+ array('key' => 'Login', 'value' => Piwik::translate('UsersManager_TheLoginScreen')),
+ array('key' => 'MultiSites', 'value' => Piwik::translate('General_AllWebsitesDashboard'), 'disabled' => empty($anonymousSites)),
+ array('key' => '1', 'value' => Piwik::translate('General_DashboardForASpecificWebsite')),
+ );
}
/**
@@ -394,6 +436,8 @@ class Controller extends ControllerAdmin
$timeFormat = Common::getRequestVar('timeformat');
$userLogin = Piwik::getCurrentUserLogin();
+ Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
+
$this->processPasswordChange($userLogin);
LanguagesManager::setLanguageForSession($language);
diff --git a/plugins/UsersManager/Menu.php b/plugins/UsersManager/Menu.php
index c95506dca3..f3844f1fa0 100644
--- a/plugins/UsersManager/Menu.php
+++ b/plugins/UsersManager/Menu.php
@@ -16,11 +16,11 @@ class Menu extends \Piwik\Plugin\Menu
public function configureAdminMenu(MenuAdmin $menu)
{
if (Piwik::isUserHasSomeAdminAccess()) {
- $menu->addManageItem('UsersManager_MenuUsers', $this->urlForAction('index'), $order = 15);
+ $menu->addSystemItem('UsersManager_MenuUsers', $this->urlForAction('index'), $order = 15);
}
if (Piwik::hasUserSuperUserAccess() && API::getInstance()->getSitesAccessFromUser('anonymous')) {
- $menu->addManageItem('UsersManager_AnonymousUser', $this->urlForAction('anonymousSettings'), $order = 16);
+ $menu->addSystemItem('UsersManager_AnonymousUser', $this->urlForAction('anonymousSettings'), $order = 16);
}
if (!Piwik::isUserIsAnonymous()) {
diff --git a/plugins/UsersManager/UsersManager.php b/plugins/UsersManager/UsersManager.php
index d8a038eb56..7e3bd11a63 100644
--- a/plugins/UsersManager/UsersManager.php
+++ b/plugins/UsersManager/UsersManager.php
@@ -92,9 +92,12 @@ class UsersManager extends \Piwik\Plugin
*/
public function getJsFiles(&$jsFiles)
{
- $jsFiles[] = "plugins/UsersManager/javascripts/usersManager.js";
- $jsFiles[] = "plugins/UsersManager/javascripts/usersSettings.js";
- $jsFiles[] = "plugins/UsersManager/javascripts/giveViewAccess.js";
+ $jsFiles[] = "plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js";
+ $jsFiles[] = "plugins/UsersManager/angularjs/personal-settings/anonymous-settings.controller.js";
+ $jsFiles[] = "plugins/UsersManager/angularjs/manage-super-user/manage-super-user.controller.js";
+ $jsFiles[] = "plugins/UsersManager/angularjs/manage-user-access/manage-user-access.controller.js";
+ $jsFiles[] = "plugins/UsersManager/angularjs/manage-users/manage-users.controller.js";
+ $jsFiles[] = "plugins/UsersManager/angularjs/give-user-view-access/give-user-view-access.controller.js";
}
/**
diff --git a/plugins/UsersManager/angularjs/give-user-view-access/give-user-view-access.controller.js b/plugins/UsersManager/angularjs/give-user-view-access/give-user-view-access.controller.js
new file mode 100644
index 0000000000..d44f145ad9
--- /dev/null
+++ b/plugins/UsersManager/angularjs/give-user-view-access/give-user-view-access.controller.js
@@ -0,0 +1,167 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('GiveUserViewAccessController', GiveUserViewAccessController);
+
+ GiveUserViewAccessController.$inject = ['piwikApi', '$window'];
+
+ function GiveUserViewAccessController(piwikApi, $window) {
+
+ var self = this;
+ this.isLoading = false;
+ this.showForm = false;
+ this.usernameOrEmail = '';
+
+ var requestOptions = {placeat: '#ajaxErrorGiveViewAccess'};
+
+ function hideLoading() {
+ self.isLoading = false;
+ }
+
+ function showLoading() {
+ self.isLoading = true;
+ }
+
+ function showErrorNotification(errorMessage)
+ {
+ var placeAt = requestOptions.placeat;
+ $(placeAt).show();
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(errorMessage, {
+ placeat: placeAt,
+ context: 'error',
+ id: 'ajaxHelper',
+ type: null
+ });
+ notification.scrollToNotification();
+ hideLoading();
+ }
+
+ function sendViewAccess(userLogin)
+ {
+ var parameters = {userLogin: userLogin, access: 'view', idSites: getIdSites()};
+
+ piwikApi.post({
+ module: 'API',
+ format: 'json',
+ method: 'UsersManager.setUserAccess'
+ }, parameters, requestOptions).then(function () {
+ $window.location.reload();
+ hideLoading();
+ }, function () {
+ hideLoading();
+ });
+ }
+ function getIdSites() {
+ return $('#usersManagerSiteSelect').attr('siteid');
+ }
+
+ function setViewAccessForUserToAllWebsitesIfUserConfirms(userLogin)
+ {
+ // ask confirmation
+ $('#confirm').find('.login').text(userLogin);
+
+ function onValidate() {
+ sendViewAccess(userLogin);
+ }
+
+ piwikHelper.modalConfirm('#confirm', {yes: onValidate, no: hideLoading})
+ }
+
+ function setViewAccessForUserIfNotAlreadyHasAccess(userLogin, idSites)
+ {
+ piwikApi.fetch({
+ method: 'UsersManager.getUsersAccessFromSite',
+ userLogin: userLogin,
+ idSite: idSites
+ }, requestOptions).then(function (users) {
+ var userLogins = [];
+ if (users) {
+ angular.forEach(users, function (val, key) {
+ userLogins.push((''+ key).toLowerCase());
+ });
+ }
+
+ if (-1 !== userLogins.indexOf(userLogin.toLowerCase())) {
+ showErrorNotification(_pk_translate('UsersManager_ExceptionUserHasViewAccessAlready'));
+ } else {
+ sendViewAccess(userLogin);
+ }
+
+ }, function () {
+ hideLoading();
+ });
+ }
+
+ function ifUserExists(usernameOrEmail)
+ {
+ return piwikApi.fetch({
+ method: 'UsersManager.userExists',
+ userLogin: usernameOrEmail,
+ }, requestOptions).then(function (response) {
+
+ return response;
+
+ }, function () {
+ hideLoading();
+ });
+ }
+
+ function getUsernameFromEmail(usernameOrEmail, callback)
+ {
+ return piwikApi.fetch({
+ method: 'UsersManager.getUserLoginFromUserEmail',
+ userEmail: usernameOrEmail,
+ }, requestOptions).then(function (response) {
+ return response;
+ }, function () {
+ hideLoading();
+ });
+ }
+
+ function giveViewAccessToUser(userLogin)
+ {
+ var idSites = getIdSites();
+
+ if (idSites === 'all') {
+ setViewAccessForUserToAllWebsitesIfUserConfirms(userLogin);
+ } else {
+ setViewAccessForUserIfNotAlreadyHasAccess(userLogin, idSites);
+ }
+ }
+
+ this.giveAccess = function () {
+
+ if (!this.usernameOrEmail) {
+ showErrorNotification(_pk_translate('UsersManager_ExceptionNoValueForUsernameOrEmail'));
+ return;
+ }
+
+ showLoading();
+
+ ifUserExists(this.usernameOrEmail).then(function (isUserName) {
+ if (isUserName && isUserName.value) {
+ giveViewAccessToUser(self.usernameOrEmail);
+ } else {
+ getUsernameFromEmail(self.usernameOrEmail).then(function (login) {
+ if (login && login.value) {
+ giveViewAccessToUser(login.value);
+ } else {
+ hideLoading();
+ }
+ });
+ }
+ });
+ };
+
+ this.showViewAccessForm = function () {
+ this.showForm = true;
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UsersManager/angularjs/manage-super-user/manage-super-user.controller.js b/plugins/UsersManager/angularjs/manage-super-user/manage-super-user.controller.js
new file mode 100644
index 0000000000..eb8c3ed9d4
--- /dev/null
+++ b/plugins/UsersManager/angularjs/manage-super-user/manage-super-user.controller.js
@@ -0,0 +1,76 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ManageSuperUserController', ManageSuperUserController);
+
+ ManageSuperUserController.$inject = ['piwikApi', '$timeout'];
+
+ function ManageSuperUserController(piwikApi, $timeout) {
+
+ var self = this;
+ this.isLoading = false;
+
+ function updateSuperUserAccess(login, hasSuperUserAccess)
+ {
+ self.isLoading = true;
+
+ $timeout(function () {
+ piwik.helper.lazyScrollTo('.loadingManageSuperUser', 40);
+ });
+
+ piwikApi.post({
+ module: 'API',
+ method: 'UsersManager.setSuperUserAccess'
+ }, {userLogin: login, hasSuperUserAccess: hasSuperUserAccess}).then(function () {
+
+ self.isLoading = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('General_Done'), {
+ placeat: '#superUserAccessUpdated',
+ context: 'success',
+ noclear: true,
+ type: 'toast',
+ style: {display: 'inline-block', marginTop: '10px', marginBottom: '30px'},
+ id: 'usersManagerSuperUserAccessUpdated'
+ });
+ notification.scrollToNotification();
+ piwikHelper.redirect();
+
+ }, function () {
+ self.isLoading = false;
+ });
+ }
+
+ this.removeSuperUserAccess = function (login) {
+ var message = 'UsersManager_ConfirmProhibitOtherUsersSuperUserAccess';
+ if (login == piwik.userLogin) {
+ message = 'UsersManager_ConfirmProhibitMySuperUserAccess';
+ }
+
+ message = _pk_translate(message, [login]);
+
+ $('#superUserAccessConfirm h2').text(message);
+
+ piwikHelper.modalConfirm('#superUserAccessConfirm', {yes: function () {
+ updateSuperUserAccess(login, 0);
+ }});
+ };
+
+ this.giveSuperUserAccess = function (login) {
+
+ var message = _pk_translate('UsersManager_ConfirmGrantSuperUserAccess', [login]);
+
+ $('#superUserAccessConfirm h2').text(message);
+
+ piwikHelper.modalConfirm('#superUserAccessConfirm', {yes: function () {
+ updateSuperUserAccess(login, 1);
+ }});
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UsersManager/angularjs/manage-user-access/manage-user-access.controller.js b/plugins/UsersManager/angularjs/manage-user-access/manage-user-access.controller.js
new file mode 100644
index 0000000000..4ed851d68d
--- /dev/null
+++ b/plugins/UsersManager/angularjs/manage-user-access/manage-user-access.controller.js
@@ -0,0 +1,96 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ManageUserAccessController', ManageUserAccessController);
+
+ ManageUserAccessController.$inject = ['piwik', 'piwikApi', '$timeout'];
+
+ function ManageUserAccessController(piwik, piwikApi, $timeout) {
+
+ var self = this;
+ this.isLoading = false;
+
+ function launchAjaxRequest(login, access, successCallback) {
+
+ self.isLoading = true;
+
+ $timeout(function () {
+ piwik.helper.lazyScrollTo('.loadingManageUserAccess', 50);
+ });
+
+ var parameters = {userLogin: login, access: access, idSites: self.site.id};
+
+ return piwikApi.post({
+ module: 'API',
+ format: 'json',
+ method: 'UsersManager.setUserAccess'
+ }, parameters).then(function (response) {
+ self.isLoading = false;
+ return response;
+ }, function () {
+ self.isLoading = false;
+ });
+ }
+
+ this.siteChanged = function () {
+ if (this.site && this.site.id != piwik.idSite) {
+ piwik.broadcast.propagateNewPage('segment=&idSite=' + this.site.id, false);
+ }
+ };
+
+ this.setAccess = function (login, access) {
+
+ // callback called when the ajax request Update the user permissions is successful
+ function successCallback(response) {
+ var mainDiv = $('[data-login="' + login + '"]');
+ mainDiv.find('.accessGranted')
+ .attr("src", "plugins/UsersManager/images/no-access.png")
+ .attr("class", "updateAccess")
+ .click(function () {
+ var access = $(this).parent().attr('id')
+ self.setAccess(login, access);
+ })
+ ;
+ mainDiv.find('#' + access + ' img')
+ .attr('src', "plugins/UsersManager/images/ok.png")
+ .attr('class', "accessGranted")
+ ;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('General_Done'), {
+ placeat: '#accessUpdated',
+ context: 'success',
+ noclear: true,
+ type: 'toast',
+ style: {display: 'inline-block', marginTop: '10px'},
+ id: 'usersManagerAccessUpdated'
+ });
+
+ // reload if user anonymous was updated, since we display a Notice message when anon has view access
+ if (login == 'anonymous') {
+ window.location.reload();
+ }
+ }
+
+ if (this.site.id == 'all') {
+
+ //ask confirmation
+ $('#confirm').find('.login').text(login);
+
+ function onValidate() {
+ launchAjaxRequest(login, access).then(successCallback);
+ }
+
+ piwikHelper.modalConfirm('#confirm', {yes: onValidate})
+ }
+ else {
+ launchAjaxRequest(login, access).then(successCallback);
+ }
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UsersManager/angularjs/manage-users/manage-users.controller.js b/plugins/UsersManager/angularjs/manage-users/manage-users.controller.js
new file mode 100644
index 0000000000..1df74622de
--- /dev/null
+++ b/plugins/UsersManager/angularjs/manage-users/manage-users.controller.js
@@ -0,0 +1,193 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ManageUsersController', ManageUsersController);
+
+ ManageUsersController.$inject = ['piwik', 'piwikApi', '$timeout'];
+
+ function ManageUsersController(piwik, piwikApi, $timeout) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+ var alreadyEdited = {};
+
+ this.isLoading = false;
+ this.showCreateUser = true;
+
+ function setIsLoading()
+ {
+ self.isLoading = true;
+ $timeout(function () {
+ piwik.helper.lazyScrollTo('.loadingManageUsers', 50);
+ });
+ }
+
+ 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();
+
+ setIsLoading();
+
+ piwikApi.post({
+ module: 'API',
+ method: 'UsersManager.updateUser'
+ }, parameters).then(function () {
+ piwik.helper.redirect();
+ self.isLoading = false;
+ }, function () {
+ self.isLoading = false;
+ });
+ }
+
+ function sendDeleteUserAJAX(login) {
+
+ setIsLoading();
+
+ piwikApi.post({
+ module: 'API',
+ method: 'UsersManager.deleteUser'
+ }, {userLogin: login}).then(function () {
+ piwik.helper.redirect();
+ self.isLoading = false;
+ }, function () {
+ self.isLoading = false;
+ });
+ }
+
+ 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();
+
+ setIsLoading();
+
+ piwikApi.post({
+ module: 'API',
+ method: 'UsersManager.addUser'
+ }, parameters).then(function () {
+ piwik.helper.redirect();
+ self.isLoading = false;
+ }, function () {
+ self.isLoading = false;
+ });
+ }
+
+ function submitOnEnter(e) {
+ var key = e.keyCode || e.which;
+ if (key == 13) {
+ $(this).find('.adduser').click();
+ $(this).find('.updateuser').click();
+ }
+ }
+
+ this.editUser = function (idRow) {
+ if (alreadyEdited[idRow] == 1) {
+ return;
+ }
+
+ alreadyEdited[idRow] = 1;
+
+ var $row = $('tr#' + idRow);
+
+ $row.find('.editable').keypress(submitOnEnter);
+ $row.find('.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);
+ }
+ }
+ );
+
+ var $delete = $row.find('.edituser');
+
+ $delete
+ .toggle()
+ .parent()
+ .prepend($('<a class="canceluser">' + _pk_translate('General_OrCancel', ['', '']) + '</a>')
+ .click(function () {
+ piwikHelper.redirect();
+ })
+ ).prepend($('<input type="submit" class="btn updateuser" value="' + _pk_translate('General_Save') + '" />')
+ .click(function () {
+ var onValidate = function () {
+ sendUpdateUserAJAX($('tr#' + idRow));
+ };
+ if ($('tr#' + idRow).find('input#password').val() != '-') {
+ piwikHelper.modalConfirm('#confirmPasswordChange', {yes: onValidate});
+ } else {
+ onValidate();
+ }
+ })
+ );
+ }
+
+ this.createUser = function () {
+ this.showCreateUser = false;
+
+ var numberOfRows = $('table#users')[0].rows.length;
+ var newRowId = numberOfRows + 1;
+ newRowId = 'row' + newRowId;
+
+ $($.parseHTML(' <tr id="' + newRowId + '" class="addNewUserRow">\
+ <td><input id="useradd_login" placeholder="username" size="10" /></td>\
+ <td><input id="useradd_password" placeholder="password" size="10" /></td>\
+ <td><input id="useradd_email" placeholder="email@domain.com" size="15" /></td>\
+ <td><input id="useradd_alias" placeholder="alias" size="15" /></td>\
+ <td>-</td>\
+ <td>-</td>\
+ <td><input type="submit" class="btn adduser" value="' + _pk_translate('General_Save') + '" /></td>\
+ <td><span class="cancel">' + sprintf(_pk_translate('General_OrCancel'), "", "") + '</span></td>\
+ </tr>'))
+ .appendTo('#users')
+ ;
+ $('#' + newRowId).keypress(submitOnEnter);
+ $('.adduser').click(function () { sendAddUserAJAX($('tr#' + newRowId)); });
+ $('.cancel').click(function () {
+ piwikHelper.hideAjaxError();
+ $(this).parents('tr').remove();
+ $('.add-user').toggle();
+ });
+ };
+
+ this.deleteUser = function (loginToDelete) {
+
+ var idRow = $(this).attr('id');
+
+ var message = _pk_translate('UsersManager_DeleteConfirm');
+ $('#confirmUserRemove').find('h2').text(sprintf(message, '"' + loginToDelete + '"'));
+
+ piwikHelper.modalConfirm('#confirmUserRemove', {yes: function () {
+ sendDeleteUserAJAX(loginToDelete);
+ }});
+ };
+
+ $(document).ready(function () {
+ var alreadyEdited = [];
+ // when click on edituser, the cells become editable
+
+ // Show the token_auth
+ $('.token_auth').click(function () {
+ var token = $(this).data('token');
+ if ($(this).text() != token) {
+ $(this).text(token);
+ }
+ });
+ });
+
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UsersManager/angularjs/personal-settings/anonymous-settings.controller.js b/plugins/UsersManager/angularjs/personal-settings/anonymous-settings.controller.js
new file mode 100644
index 0000000000..9c4e30e1e2
--- /dev/null
+++ b/plugins/UsersManager/angularjs/personal-settings/anonymous-settings.controller.js
@@ -0,0 +1,47 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('AnonymousSettingsController', AnonymousSettingsController);
+
+ AnonymousSettingsController.$inject = ['piwikApi'];
+
+ function AnonymousSettingsController(piwikApi) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+
+ function updateSettings(postParams)
+ {
+ self.loading = true;
+
+ piwikApi.withTokenInUrl();
+ piwikApi.post({
+ module: 'UsersManager', action: 'recordAnonymousUserSettings', format: 'json'
+ }, postParams).then(function (success) {
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
+ id: 'anonymousUserSettings', context: 'success'});
+ notification.scrollToNotification();
+
+ self.loading = false;
+ }, function (errorMessage) {
+ self.loading = false;
+ });
+ }
+
+ this.save = function () {
+
+ var postParams = {
+ anonymousDefaultReport: this.defaultReport == '1' ? this.defaultReportWebsite : this.defaultReport,
+ anonymousDefaultDate: this.defaultDate
+ };
+
+ updateSettings(postParams);
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js b/plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js
new file mode 100644
index 0000000000..a14c47390a
--- /dev/null
+++ b/plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js
@@ -0,0 +1,64 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('PersonalSettingsController', PersonalSettingsController);
+
+ PersonalSettingsController.$inject = ['piwikApi'];
+
+ function PersonalSettingsController(piwikApi) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+
+ var self = this;
+
+ function updateSettings(postParams)
+ {
+ self.loading = true;
+
+ piwikApi.withTokenInUrl();
+ piwikApi.post({
+ module: 'UsersManager', action: 'recordUserSettings', format: 'json'
+ }, postParams).then(function (success) {
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
+ id: 'PersonalSettingsSuccess', context: 'success'});
+ notification.scrollToNotification();
+
+ self.loading = false;
+ }, function (errorMessage) {
+ self.loading = false;
+ });
+ }
+
+ this.save = function () {
+
+ var postParams = {
+ alias: this.alias,
+ email: this.email,
+ defaultReport: this.defaultReport == 'MultiSites' ? this.defaultReport : this.site.id,
+ defaultDate: this.defaultDate,
+ language: this.language,
+ timeformat: this.timeformat,
+ };
+
+ if (this.passwordBis) {
+ postParams.passwordBis = this.passwordBis;
+ }
+
+ if (this.password) {
+ postParams.password = this.password;
+
+ piwikHelper.modalConfirm('#confirmPasswordChange', {yes: function () {
+ updateSettings(postParams);
+ }});
+ } else {
+ updateSettings(postParams);
+ }
+
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/UsersManager/javascripts/giveViewAccess.js b/plugins/UsersManager/javascripts/giveViewAccess.js
deleted file mode 100644
index 3db1bc3889..0000000000
--- a/plugins/UsersManager/javascripts/giveViewAccess.js
+++ /dev/null
@@ -1,176 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-$(document).ready(function () {
-
- function hideLoading()
- {
- $('#giveUserAccessToViewReports').prop('disabled', false);
- $('#ajaxLoadingGiveViewAccess').hide();
- }
-
- function showLoading()
- {
- $('#giveUserAccessToViewReports').prop('disabled', true);
- $('#ajaxLoadingGiveViewAccess').show();
- }
-
- function showErrorNotification(errorMessage)
- {
- var placeAt = '#ajaxErrorGiveViewAccess';
- $(placeAt).show();
-
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
- notification.show(errorMessage, {
- placeat: placeAt,
- context: 'error',
- id: 'ajaxHelper',
- type: null
- });
- notification.scrollToNotification();
- hideLoading();
- }
-
- function createNewAjaxHelper()
- {
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.setCompleteCallback(function (xhr, status) {
- if (xhr &&
- xhr.responseJSON &&
- xhr.responseJSON.message &&
- xhr.responseJSON.result &&
- xhr.responseJSON.result == 'error') {
- hideLoading();
- }
- if (status && String(status).toLowerCase() !== 'sucess') {
- hideLoading();
- }
- });
- ajaxHandler.addParams({
- module: 'API',
- format: 'json'
- }, 'GET');
- ajaxHandler.setErrorElement('#ajaxErrorGiveViewAccess');
-
- return ajaxHandler;
- }
-
- function sendViewAccess(userLogin)
- {
- sendUpdateUserAccess(userLogin, 'view', function () { window.location.reload(); });
- setTimeout(hideLoading, 250);
- // we hide loading after a bit since we cannot influence the ajax request in case of any error
- }
-
- function setViewAccessForUserToAllWebsitesIfUserConfirms(userLogin)
- {
- // ask confirmation
- $('#confirm').find('#login').text(userLogin);
-
- function onValidate() {
- sendViewAccess(userLogin);
- }
-
- piwikHelper.modalConfirm('#confirm', {yes: onValidate, no: hideLoading})
- }
-
- function setViewAccessForUserIfNotAlreadyHasAccess(userLogin, idSites)
- {
- var ajaxHandler = createNewAjaxHelper();
- ajaxHandler.addParams({
- method: 'UsersManager.getUsersAccessFromSite',
- userLogin: userLogin,
- idSite: idSites
- }, 'GET');
- ajaxHandler.setCallback(function (users) {
- var userLogins = [];
- if (users && users[0]) {
- userLogins = $.map(users[0], function (val, key) {
- return (''+ key).toLowerCase();
- });
- }
-
- if (-1 !== userLogins.indexOf(userLogin.toLowerCase())) {
- showErrorNotification(_pk_translate('UsersManager_ExceptionUserHasViewAccessAlready'));
- } else {
- sendViewAccess(userLogin);
- }
-
- });
- ajaxHandler.send();
- }
-
- function ifUserExists(usernameOrEmail, callback)
- {
- var ajaxHandler = createNewAjaxHelper();
- ajaxHandler.addParams({
- method: 'UsersManager.userExists',
- userLogin: usernameOrEmail,
- }, 'GET');
- ajaxHandler.setCallback(callback);
- ajaxHandler.send();
- }
-
- function getUsernameFromEmail(usernameOrEmail, callback)
- {
- var ajaxHandler = createNewAjaxHelper();
- ajaxHandler.addParams({
- method: 'UsersManager.getUserLoginFromUserEmail',
- userEmail: usernameOrEmail,
- }, 'GET');
- ajaxHandler.setCallback(callback);
- ajaxHandler.send();
- }
-
- function giveViewAccessToUser(userLogin)
- {
- var idSites = getIdSites();
-
- if (idSites === 'all') {
- setViewAccessForUserToAllWebsitesIfUserConfirms(userLogin);
- } else {
- setViewAccessForUserIfNotAlreadyHasAccess(userLogin, idSites);
- }
- }
-
- $('#showGiveViewAccessForm').click(function () {
- $('#giveViewAccessForm').toggle()
- });
-
- $('#giveViewAccessForm #user_invite').keypress(function (e) {
- var key = e.keyCode || e.which;
- if (key == 13) {
- $('#giveViewAccessForm #giveUserAccessToViewReports').click();
- }
- });
-
- $('#giveViewAccessForm #giveUserAccessToViewReports').click(function () {
- showLoading();
-
- var usernameOrEmail = $('#user_invite').val();
-
- if (!usernameOrEmail) {
- showErrorNotification(_pk_translate('UsersManager_ExceptionNoValueForUsernameOrEmail'));
- return;
- }
-
- ifUserExists(usernameOrEmail, function (isUserName) {
- if (isUserName && isUserName.value) {
- giveViewAccessToUser(usernameOrEmail);
- } else {
- getUsernameFromEmail(usernameOrEmail, function (login) {
- if (login && login.value) {
- giveViewAccessToUser(login.value);
- } else {
- hideLoading();
- }
- });
- }
- });
- });
-});
diff --git a/plugins/UsersManager/javascripts/usersManager.js b/plugins/UsersManager/javascripts/usersManager.js
deleted file mode 100644
index 5624cb272a..0000000000
--- a/plugins/UsersManager/javascripts/usersManager.js
+++ /dev/null
@@ -1,312 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-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({
- module: 'API',
- format: 'json',
- method: 'UsersManager.updateUser'
- }, 'GET');
- ajaxHandler.addParams(parameters, 'POST');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement();
- ajaxHandler.send(true);
-}
-
-function sendDeleteUserAJAX(login) {
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'UsersManager.deleteUser'
- }, 'GET');
- ajaxHandler.addParams({userLogin: login}, 'POST');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement('#ajaxLoadingUsersManagement');
- ajaxHandler.setErrorElement('#ajaxErrorUsersManagement');
- 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();
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'UsersManager.addUser'
- }, 'GET');
- ajaxHandler.addParams(parameters, 'POST');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement('#ajaxLoadingUsersManagement');
- ajaxHandler.setErrorElement('#ajaxErrorUsersManagement');
- ajaxHandler.send(true);
-}
-
-function getIdSites() {
- return $('#usersManagerSiteSelect').attr('siteid');
-}
-
-function sendUpdateUserAccess(login, access, successCallback) {
- var parameters = {};
- parameters.userLogin = login;
- parameters.access = access;
- parameters.idSites = getIdSites();
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'UsersManager.setUserAccess'
- }, 'GET');
- ajaxHandler.addParams(parameters, 'POST');
- ajaxHandler.setCallback(successCallback);
- ajaxHandler.setLoadingElement('#ajaxLoadingUsersManagement');
- ajaxHandler.setErrorElement('#ajaxErrorUsersManagement');
- ajaxHandler.send(true);
-}
-
-function submitOnEnter(e) {
- var key = e.keyCode || e.which;
- if (key == 13) {
- $(this).find('.adduser').click();
- $(this).find('.updateuser').click();
- }
-}
-
-function launchAjaxRequest(self, successCallback) {
- sendUpdateUserAccess(
- $(self).parent().parent().find('#login').html(), //if changed change also the modal
- $(self).parent().attr('id'),
- successCallback
- );
-}
-
-function updateSuperUserAccess(login, hasSuperUserAccess)
-{
- var parameters = {};
- parameters.userLogin = login;
- parameters.hasSuperUserAccess = hasSuperUserAccess ? 1: 0;
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'UsersManager.setSuperUserAccess'
- }, 'GET');
- ajaxHandler.addParams(parameters, 'POST');
- ajaxHandler.setCallback(function () {
-
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
- notification.show(_pk_translate('General_Done'), {
- placeat: '#superUserAccessUpdated',
- context: 'success',
- noclear: true,
- type: 'toast',
- style: {display: 'inline-block', marginTop: '10px', marginBottom: '30px'},
- id: 'usersManagerSuperUserAccessUpdated'
- });
- notification.scrollToNotification();
- piwikHelper.redirect();
- });
- ajaxHandler.setLoadingElement('#ajaxErrorSuperUsersManagement');
- ajaxHandler.setErrorElement('#ajaxErrorSuperUsersManagement');
- ajaxHandler.send(true);
-}
-
-function bindUpdateSuperUserAccess() {
- var login = $(this).parents('td').data('login');
- var hasAccess = parseInt($(this).data('hasaccess'), 10);
-
- var message = 'UsersManager_ConfirmGrantSuperUserAccess';
- if (hasAccess && login == piwik.userLogin) {
- message = 'UsersManager_ConfirmProhibitMySuperUserAccess';
- } else if (hasAccess) {
- message = 'UsersManager_ConfirmProhibitOtherUsersSuperUserAccess';
- }
-
- message = _pk_translate(message, [login]);
-
- $('#superUserAccessConfirm h2').text(message);
-
- piwikHelper.modalConfirm('#superUserAccessConfirm', {yes: function () {
- updateSuperUserAccess(login, !hasAccess);
- }});
-}
-
-function bindUpdateAccess() {
- var self = this;
- // 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")
- .click(bindUpdateAccess)
- ;
- $(self)
- .attr('src', "plugins/UsersManager/images/ok.png")
- .attr('class', "accessGranted")
- ;
-
- var UI = require('piwik/UI');
- var notification = new UI.Notification();
- notification.show(_pk_translate('General_Done'), {
- placeat: '#accessUpdated',
- context: 'success',
- noclear: true,
- type: 'toast',
- style: {display: 'inline-block', marginTop: '10px'},
- id: 'usersManagerAccessUpdated'
- });
-
- // reload if user anonymous was updated, since we display a Notice message when anon has view access
- 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);
- }
-}
-
-$(document).ready(function () {
- var alreadyEdited = [];
- // 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($('<a class="canceluser">' + _pk_translate('General_OrCancel', ['', '']) + '</a>')
- .click(function () {
- piwikHelper.redirect();
- })
- ).prepend($('<input type="submit" class="submit updateuser" value="' + _pk_translate('General_Save') + '" />')
- .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').find('h2').text(sprintf(_pk_translate('UsersManager_DeleteConfirm'), '"' + loginToDelete + '"'));
- piwikHelper.modalConfirm('#confirmUserRemove', {yes: function () { sendDeleteUserAJAX(loginToDelete); }});
- }
- );
-
- $('.admin .user .add-user').click(function () {
- piwikHelper.hideAjaxError();
- $(this).toggle();
-
- var numberOfRows = $('table#users')[0].rows.length;
- var newRowId = numberOfRows + 1;
- newRowId = 'row' + newRowId;
-
- $($.parseHTML(' <tr id="' + newRowId + '">\
- <td><input id="useradd_login" placeholder="username" size="10" /></td>\
- <td><input id="useradd_password" placeholder="password" size="10" /></td>\
- <td><input id="useradd_email" placeholder="email@domain.com" size="15" /></td>\
- <td><input id="useradd_alias" placeholder="alias" size="15" /></td>\
- <td>-</td>\
- <td>-</td>\
- <td><input type="submit" class="submit adduser" value="' + _pk_translate('General_Save') + '" /></td>\
- <td><span class="cancel">' + sprintf(_pk_translate('General_OrCancel'), "", "") + '</span></td>\
- </tr>'))
- .appendTo('#users')
- ;
- $('#' + newRowId).keypress(submitOnEnter);
- $('.adduser').click(function () { sendAddUserAJAX($('tr#' + newRowId)); });
- $('.cancel').click(function () {
- piwikHelper.hideAjaxError();
- $(this).parents('tr').remove();
- $('.add-user').toggle();
- });
- });
-
- $('#access .updateAccess').click(bindUpdateAccess);
-
- $('#superUserAccess .accessGranted, #superUserAccess .updateAccess').click(bindUpdateSuperUserAccess);
-
- // when a site is selected, reload the page w/o showing the ajax loading element
- $('#usersManagerSiteSelect').bind('change', function (e, site) {
- if (site.id != piwik.idSite) {
- piwik.broadcast.propagateNewPage('segment=&idSite=' + site.id, false);
- }
- });
-
- // Show the token_auth
- $('.token_auth').click(function () {
- var token = $(this).data('token');
- if ($(this).text() != token) {
- $(this).text(token);
- }
- });
-});
diff --git a/plugins/UsersManager/javascripts/usersSettings.js b/plugins/UsersManager/javascripts/usersSettings.js
deleted file mode 100644
index 1708e64bf7..0000000000
--- a/plugins/UsersManager/javascripts/usersSettings.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-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').attr('siteid');
- }
- var postParams = {};
- postParams.alias = alias;
- postParams.email = email;
- if (password) {
- postParams.password = password;
- }
- if (passwordBis) {
- postParams.passwordBis = passwordBis;
- }
- postParams.defaultReport = defaultReport;
- postParams.defaultDate = defaultDate;
- postParams.language = $('#userSettingsTable #language').val();
- postParams.timeformat = $('#userSettingsTable #timeformat').val();
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'UsersManager',
- format: 'json',
- action: 'recordUserSettings'
- }, 'GET');
- ajaxHandler.withTokenInUrl();
- ajaxHandler.addParams(postParams, 'POST');
- ajaxHandler.redirectOnSuccess(params);
- ajaxHandler.setLoadingElement('#ajaxLoadingUserSettings');
- ajaxHandler.setErrorElement('#ajaxErrorUserSettings');
- ajaxHandler.send(true);
-}
-function sendAnonymousUserSettingsAJAX() {
- var anonymousDefaultReport = $('input[name=anonymousDefaultReport]:checked').val();
- if (anonymousDefaultReport == 1) {
- anonymousDefaultReport = $('#anonymousDefaultReportWebsite').find('option:selected').val();
- }
- var anonymousDefaultDate = $('input[name=anonymousDefaultDate]:checked').val();
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'UsersManager',
- format: 'json',
- action: 'recordAnonymousUserSettings'
- }, 'GET');
- ajaxHandler.withTokenInUrl();
- ajaxHandler.addParams({
- anonymousDefaultReport: anonymousDefaultReport,
- anonymousDefaultDate: anonymousDefaultDate
- }, 'POST');
- ajaxHandler.redirectOnSuccess();
- ajaxHandler.setLoadingElement('#ajaxLoadingAnonymousUserSettings');
- ajaxHandler.setErrorElement('#ajaxErrorAnonymousUserSettings');
- ajaxHandler.send(true);
-}
-
-$(document).ready(function () {
- $('#userSettingsSubmit').click(function () {
- if ($('#password').length > 0 && $('#password').val() != '') {
- piwikHelper.modalConfirm('#confirmPasswordChange', {yes: sendUserSettingsAJAX});
- } else {
- sendUserSettingsAJAX();
- }
-
- });
- $('#userSettingsTable').find('input').keypress(function (e) {
- var key = e.keyCode || e.which;
- if (key == 13) {
- $('#userSettingsSubmit').click();
- }
- });
-
- $('#anonymousUserSettingsSubmit').click(function () {
- sendAnonymousUserSettingsAJAX();
- });
-});
diff --git a/plugins/UsersManager/lang/en.json b/plugins/UsersManager/lang/en.json
index a4a07421c0..7111d6acb0 100644
--- a/plugins/UsersManager/lang/en.json
+++ b/plugins/UsersManager/lang/en.json
@@ -37,7 +37,7 @@
"ExceptionNoValueForUsernameOrEmail": "Please enter a username or email address.",
"ExcludeVisitsViaCookie": "Exclude your visits using a cookie",
"ForAnonymousUsersReportDateToLoadByDefault": "For anonymous users, report date to load by default",
- "GiveViewAccess": "Give view access",
+ "GiveViewAccess": "Give view access for %1$s",
"GiveViewAccessTitle": "Give an existing user access to view reports for %s",
"GiveViewAccessInstructions": "To give an existing user view access for %s enter the username or email address of an existing user",
"IfYouWouldLikeToChangeThePasswordTypeANewOne": "If you would like to change the password type a new one. Otherwise leave this blank.",
diff --git a/plugins/UsersManager/stylesheets/usersManager.less b/plugins/UsersManager/stylesheets/usersManager.less
index f8f0f9de92..90355b5b4f 100644
--- a/plugins/UsersManager/stylesheets/usersManager.less
+++ b/plugins/UsersManager/stylesheets/usersManager.less
@@ -1,6 +1,8 @@
#users .editable:hover,
#users .addrow:hover,
-#users .updateAccess:hover,
+#superUserAccess .updateAccess:hover,
+#superUserAccess .accessGranted:hover,
+#manageUserAccess .updateAccess:hover,
#users .accessGranted:hover,
#users .adduser:hover, .edituser:hover,
#users .deleteuser:hover,
@@ -9,6 +11,14 @@
cursor: pointer;
}
+#users .addNewUserRow td {
+ padding: 0 16px;
+}
+
+#users td {
+ vertical-align: middle !important;
+}
+
#userSettingsTable {
.sites_autocomplete {
margin-left: 20px;
@@ -16,6 +26,10 @@
}
}
+#users #token_auth_user {
+ border-color: @theme-color-background-tinyContrast;
+}
+
#users .canceluser {
margin-top: 10px;
margin-bottom: 10px;
@@ -48,8 +62,11 @@
text-align: left;
}
+#ajaxErrorGiveViewAccess {
+ margin-top: 16px;
+}
+
#giveViewAccessForm {
- display: none;
margin-left: 30px;
#user_invite {
diff --git a/plugins/UsersManager/templates/anonymousSettings.twig b/plugins/UsersManager/templates/anonymousSettings.twig
index 32c606499f..1050bf52d3 100644
--- a/plugins/UsersManager/templates/anonymousSettings.twig
+++ b/plugins/UsersManager/templates/anonymousSettings.twig
@@ -4,9 +4,7 @@
{% block content %}
{% if isSuperUser %}
-
- <h2 piwik-enriched-headline>{{ title }}</h2>
-
+<div piwik-content-block content-title="{{ title|e('html_attr') }}">
{% if anonymousSites|length == 0 %}
<div class="alert alert-info">
@@ -15,64 +13,33 @@
{% else %}
- {{ ajax.errorDiv('ajaxErrorAnonymousUserSettings') }}
- {{ ajax.loadingDiv('ajaxLoadingAnonymousUserSettings') }}
+ <div piwik-form ng-controller="AnonymousSettingsController as anonymousSettings">
- <div class="form-group">
- <label>
- {{ 'UsersManager_WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess'|translate }}
- </label>
- <fieldset>
- <label class="radio">
- <input id="anonymousDefaultReport-login" type="radio" value="Login"
- name="anonymousDefaultReport"
- {% if anonymousDefaultReport==loginModule %} checked="checked"{% endif %} />
- {{ 'UsersManager_TheLoginScreen'|translate }}
- </label>
- <label class="radio">
- <input id="anonymousDefaultReport-multisites"
- {% if anonymousSites is empty %}disabled="disabled" {% endif %}
- type="radio" value="MultiSites" name="anonymousDefaultReport"
- {% if anonymousDefaultReport=='MultiSites' %} checked="checked"{% endif %} />
- {{ 'General_AllWebsitesDashboard'|translate }}
- </label>
+ <div piwik-field uicontrol="radio" name="anonymousDefaultReport"
+ ng-model="anonymousSettings.defaultReport"
+ introduction="{{ 'UsersManager_WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess'|translate|e('html_attr') }}"
+ value="{{ anonymousDefaultReport }}"
+ options="{{ defaultReportOptions|json_encode }}">
+ </div>
- <label class="radio">
- <input id="anonymousDefaultReport-specific"
- {% if anonymousSites is empty %}disabled="disabled" {% endif %}
- type="radio" value="1" name="anonymousDefaultReport"
- {% if anonymousDefaultReport>0 %} checked="checked"{% endif %} />
- {{ 'General_DashboardForASpecificWebsite'|translate }}
+ <div piwik-field uicontrol="select" name="anonymousDefaultReportWebsite"
+ ng-model="anonymousSettings.defaultReportWebsite"
+ options="{{ anonymousSites|json_encode }}"
+ value="{{ anonymousDefaultSite }}">
+ </div>
- {% if anonymousSites is not empty %}
- <select id="anonymousDefaultReportWebsite">
- {% for info in anonymousSites %}
- <option value="{{ info.idsite }}" {% if anonymousDefaultReport==info.idsite %} selected="selected"{% endif %}>
- {{ info.name|raw }}
- </option>
- {% endfor %}
- </select>
- {% endif %}
- </label>
+ <div piwik-field uicontrol="radio" name="anonymousDefaultDate"
+ ng-model="anonymousSettings.defaultDate"
+ introduction="{{ 'UsersManager_ForAnonymousUsersReportDateToLoadByDefault'|translate|e('html_attr') }}"
+ value="{{ anonymousDefaultDate }}"
+ options="{{ availableDefaultDates|json_encode }}">
+ </div>
- </fieldset>
- </div>
+ <div piwik-save-button saving="anonymousSettings.loading" onconfirm="anonymousSettings.save()"></div>
- <div class="form-group">
- <label>{{ 'UsersManager_ForAnonymousUsersReportDateToLoadByDefault'|translate }}</label>
- <fieldset>
- {% for value,description in availableDefaultDates %}
- <label class="radio">
- <input type="radio" name="anonymousDefaultDate"
- {% if anonymousDefaultDate==value %}checked="checked" {% endif %}value="{{ value }}"/>
- {{ description }}
- </label>
- {% endfor %}
- </fieldset>
</div>
- <input type="submit" value="{{ 'General_Save'|translate }}" id="anonymousUserSettingsSubmit" class="submit"/>
-
{% endif %}
+</div>
{% endif %}
{% endblock %} \ No newline at end of file
diff --git a/plugins/UsersManager/templates/index.twig b/plugins/UsersManager/templates/index.twig
index d6f6edfdbd..b4f75df6cc 100644
--- a/plugins/UsersManager/templates/index.twig
+++ b/plugins/UsersManager/templates/index.twig
@@ -4,43 +4,51 @@
{% block content %}
-<h2 piwik-enriched-headline
- help-url="http://piwik.org/docs/manage-users/">{{ title }}</h2>
-<div id="sites" class="usersManager">
- <section class="sites_selector_container">
- <p>{{ 'UsersManager_MainDescription'|translate }}</p>
-
- {% set applyAllSitesText %}
- <strong>{{ 'UsersManager_ApplyToAllWebsites'|translate }}</strong>
- {% endset %}
-
- <div piwik-siteselector
- show-selected-site="true"
- only-sites-with-admin-access="true"
- class="sites_autocomplete"
- siteid="{{ idSiteSelected }}"
- sitename="{{ defaultReportSiteName }}"
- all-sites-text="{{ applyAllSitesText|raw }}"
- all-sites-location="top"
- id="usersManagerSiteSelect"
- switch-site-on-select="false"></div>
- </section>
-</div>
+<div piwik-content-block
+ content-title="{{ title|e('html_attr') }}"
+ feature="true"
+ style="width:800px;"
+ help-url="https://piwik.org/docs/manage-users/"
+ >
+<div ng-controller="ManageUserAccessController as manageUserAccess">
+ <div id="sites" class="usersManager">
+ <section class="sites_selector_container">
+ <p>{{ 'UsersManager_MainDescription'|translate }}</p>
+
+ {% set applyAllSitesText %}
+ <strong>{{ 'UsersManager_ApplyToAllWebsites'|translate }}</strong>
+ {% endset %}
+
+ <div piwik-siteselector
+ show-selected-site="true"
+ only-sites-with-admin-access="true"
+ class="sites_autocomplete"
+ ng-model="manageUserAccess.site"
+ ng-change="manageUserAccess.siteChanged()"
+ siteid="{{ idSiteSelected }}"
+ sitename="{{ defaultReportSiteName }}"
+ all-sites-text="{{ applyAllSitesText|raw }}"
+ all-sites-location="top"
+ id="usersManagerSiteSelect"
+ switch-site-on-select="false"></div>
+ </section>
+ </div>
-{% block websiteAccessTable %}
+ {% block websiteAccessTable %}
-{% import 'ajaxMacros.twig' as ajax %}
-{{ ajax.errorDiv }}
-{{ ajax.loadingDiv }}
+ {% import 'ajaxMacros.twig' as ajax %}
+
+ <div piwik-activity-indicator class="loadingManageUserAccess" loading="manageUserAccess.isLoading"></div>
+ <div id="accessUpdated" style="vertical-align:top;"></div>
-<div class="entityContainer" style="width:600px;margin-top:16px;">
{% if anonymousHasViewAccess %}
<br/>
<div class="alert alert-warning">
{{ ['UsersManager_AnonymousUserHasViewAccess'|translate("'anonymous'","'view'"), 'UsersManager_AnonymousUserHasViewAccess2'|translate]|join(' ') }}
</div>
{% endif %}
- <table class="entityTable dataTable" id="access" style="display:inline-table;width:550px;">
+
+ <table piwik-content-table id="manageUserAccess">
<thead>
<tr>
<th class='first'>{{ 'UsersManager_User'|translate }}</th>
@@ -53,11 +61,11 @@
<tbody>
{% set accesValid %}<img src='plugins/UsersManager/images/ok.png' class='accessGranted' />{% endset %}
- {% set accesInvalid %}<img src='plugins/UsersManager/images/no-access.png' class='updateAccess' />{% endset %}
{% set superUserAccess %}<span title="{{ 'UsersManager_ExceptionSuperUserAccess'|translate }}">N/A</span>{% endset %}
+
{% for login,access in usersAccessByWebsite %}
{% if userIsSuperUser or (hasOnlyAdminAccess and (access!='noaccess' or idSiteSelected == 'all')) %}
- <tr>
+ <tr data-login="{{ login|e('html_attr') }}">
<td id='login'>{{ login }}</td>
<td>{{ usersAliasByLogin[login]|raw }}</td>
<td id='noaccess'>
@@ -66,7 +74,9 @@
{% elseif access=='noaccess' and idSiteSelected != 'all' %}
{{ accesValid }}
{% else %}
- {{ accesInvalid }}
+ <img src='plugins/UsersManager/images/no-access.png' class='updateAccess'
+ ng-click='manageUserAccess.setAccess({{ login|json_encode}}, "noaccess")'
+ />
{% endif %}&nbsp;</td>
<td id='view'>
{% if login in superUserLogins %}
@@ -74,7 +84,9 @@
{% elseif access == 'view' and idSiteSelected != 'all' %}
{{ accesValid }}
{% else %}
- {{ accesInvalid }}
+ <img src='plugins/UsersManager/images/no-access.png' class='updateAccess'
+ ng-click='manageUserAccess.setAccess({{ login|json_encode}}, "view")'
+ />
{% endif %}&nbsp;</td>
<td id='admin'>
{% if login in superUserLogins %}
@@ -82,7 +94,13 @@
{% elseif login == 'anonymous' %}
N/A
{% else %}
- {% if access == 'admin' and idSiteSelected != 'all' %}{{ accesValid }}{% else %}{{ accesInvalid }}{% endif %}&nbsp;
+ {% if access == 'admin' and idSiteSelected != 'all' %}
+ {{ accesValid }}
+ {% else %}
+ <img src='plugins/UsersManager/images/no-access.png' class='updateAccess'
+ ng-click='manageUserAccess.setAccess({{ login|json_encode}}, "admin")'
+ />
+ {% endif %}&nbsp;
{% endif %}
</td>
</tr>
@@ -90,37 +108,47 @@
{% endfor %}
</tbody>
</table>
- <div id="accessUpdated" style="vertical-align:top;"></div>
-</div>
-{% if hasOnlyAdminAccess %}
- <p>
- <button id="showGiveViewAccessForm" class="add-user btn btn-lg btn-flat">
- <span class="icon-add"></span>
- {{ 'UsersManager_GiveViewAccessTitle'|translate('"' ~ defaultReportSiteName ~ '"')|raw }}
- </button>
- </p>
- <form id="giveViewAccessForm">
- <div class="form-group">
- <input type="text" name="user_invite"
- id="user_invite"
- placeholder="{{ 'UsersManager_EnterUsernameOrEmail'|translate|e('html_attr') }}"
- title="{{ 'UsersManager_GiveViewAccessInstructions'|translate("'" ~ defaultReportSiteName ~ "'")|e('html_attr') }}">
+ {% if hasOnlyAdminAccess %}
+ <div class="tableActionBar">
+ <div ng-controller="GiveUserViewAccessController as giveViewAccess" piwik-form>
+ <button id="showGiveViewAccessForm"
+ ng-show="!giveViewAccess.showForm" ng-click="giveViewAccess.showViewAccessForm()">
+ <span class="icon-add"></span>
+ {{ 'UsersManager_GiveViewAccessTitle'|translate('"' ~ defaultReportSiteName ~ '"')|raw }}
+ </button>
+
+ <form id="giveViewAccessForm" ng-show="giveViewAccess.showForm">
+ <div piwik-field uicontrol="text" name="user_invite"
+ ng-model="giveViewAccess.usernameOrEmail"
+ full-width="true"
+ title="{{ 'UsersManager_EnterUsernameOrEmail'|translate|e('html_attr') }}"
+ >
+ </div>
+
+ <div piwik-save-button id="giveUserAccessToViewReports"
+ onconfirm="giveViewAccess.giveAccess()"
+ saving="giveViewAccess.isLoading"
+ value="{{ 'UsersManager_GiveViewAccess'|translate("'" ~ defaultReportSiteName ~ "'")|e('html_attr') }}"></div>
+
+ </form>
+ </div>
</div>
+ <div id="ajaxErrorGiveViewAccess">
- <input class="btn" type="button" id="giveUserAccessToViewReports" value="{{ 'UsersManager_GiveViewAccess'|translate|e('html_attr') }}">
- </form>
- {{ ajax.errorDiv('ajaxErrorGiveViewAccess') }}
- {{ ajax.loadingDiv('ajaxLoadingGiveViewAccess') }}
-{% endif %}
+ </div>
+ {% endif %}
+</div>
+</div>
<div class="ui-confirm" id="confirm">
- <h2>{{ 'UsersManager_ChangeAllConfirm'|translate("<span id='login'></span>")|raw }}</h2>
+ <h2>{{ 'UsersManager_ChangeAllConfirm'|translate("<span class='login'></span>")|raw }}</h2>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
{% if userIsSuperUser %}
+<div piwik-content-block content-title="{{ 'UsersManager_UsersManagement'|translate|e('html_attr') }}">
<div class="ui-confirm" id="confirmUserRemove">
<h2></h2>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
@@ -132,14 +160,14 @@
<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>")|raw }}</p>
{% import 'ajaxMacros.twig' as ajax %}
- {{ ajax.errorDiv('ajaxErrorUsersManagement') }}
- {{ ajax.loadingDiv('ajaxLoadingUsersManagement') }}
- <div class="user entityContainer" style="margin-bottom:50px;">
- <table class="entityTable dataTable" id="users">
+
+ <div class="user" ng-controller="ManageUsersController as manageUsers">
+ <div piwik-activity-indicator class="loadingManageUsers" loading="manageUsers.isLoading"></div>
+
+ <table piwik-content-table id="users">
<thead>
<tr>
<th>{{ 'General_Username'|translate }}</th>
@@ -159,21 +187,24 @@
{% for i,user in users %}
{% 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|raw }}</td>
- <td id="token_auth" class="token_auth" data-token="{{ user.token_auth }}">{{ user.token_auth|slice(0, 8) }}…</td>
+ <td id="userLogin" class="editable" ng-click='manageUsers.editUser("row{{ i|e('js') }}")'>{{ user.login }}</td>
+ <td id="password" class="editable" ng-click='manageUsers.editUser("row{{ i|e('js') }}")'>-</td>
+ <td id="email" class="editable" ng-click='manageUsers.editUser("row{{ i|e('js') }}")'>{{ user.email }}</td>
+ <td id="alias" class="editable" ng-click='manageUsers.editUser("row{{ i|e('js') }}")'>{{ user.alias|raw }}</td>
+ <td id="token_auth_user" class="token_auth" data-token="{{ user.token_auth }}">{{ user.token_auth|slice(0, 8) }}…</td>
{% if user.last_seen is defined %}
<td id="last_seen">{% if user.last_seen is empty %}-{% else %}{{ 'General_TimeAgo'|translate(user.last_seen)|raw }}{% endif %}</td>
{% endif %}
- <td class="text-center">
- <button class="edituser btn btn-flat" id="row{{ i }}" title="{{ 'General_Edit'|translate }}">
+ <td class="center">
+ <button ng-click='manageUsers.editUser("row{{ i|e('js') }}")'
+ class="edituser table-action" title="{{ 'General_Edit'|translate }}">
<span class="icon-edit"></span>
</button>
</td>
- <td class="text-center">
- <button class="deleteuser btn btn-flat" id="row{{ i }}" title="{{ 'General_Delete'|translate }}">
+ <td class="center">
+ <button class="deleteuser table-action"
+ ng-click='manageUsers.deleteUser({{ user.login|json_encode }})'
+ title="{{ 'General_Delete'|translate }}">
<span class="icon-delete"></span>
</button>
</td>
@@ -182,60 +213,76 @@
{% endfor %}
</tbody>
</table>
- <p>
- <button class="add-user btn btn-lg btn-flat">
+
+ <div class="tableActionBar">
+ <button class="add-user" ng-click="manageUsers.createUser()" ng-show="manageUsers.showCreateUser">
<span class="icon-add"></span>
{{ 'UsersManager_AddUser'|translate }}
</button>
- </p>
+ </div>
</div>
+</div>
- <h2 id="super_user_access">{{ 'UsersManager_SuperUserAccessManagement'|translate }}</h2>
- <p>{{ 'UsersManager_SuperUserAccessManagementMainDescription'|translate }} <br/>
- {{ 'UsersManager_SuperUserAccessManagementGrantMore'|translate }}</p>
+<div piwik-content-block
+ id="super_user_access"
+ style="width:800px;"
+ content-title="{{ 'UsersManager_SuperUserAccessManagement'|translate|e('html_attr') }}">
- {{ ajax.errorDiv('ajaxErrorSuperUsersManagement') }}
- {{ ajax.loadingDiv('ajaxLoadingSuperUsersManagement') }}
+ <div ng-controller="ManageSuperUserController as manageSuperUser">
- <table class="entityTable dataTable" id="superUserAccess" style="display:inline-table;width:400px;">
- <thead>
- <tr>
- <th class='first'>{{ 'UsersManager_User'|translate }}</th>
- <th>{{ 'UsersManager_Alias'|translate }}</th>
- <th>{{ 'Installation_SuperUser'|translate }}</th>
- </tr>
- </thead>
+ <p>{{ 'UsersManager_SuperUserAccessManagementMainDescription'|translate }} <br/>
+ {{ 'UsersManager_SuperUserAccessManagementGrantMore'|translate }}</p>
- <tbody>
- {% if users|length > 1 %}
- {% for login,alias in usersAliasByLogin if login != 'anonymous' %}
+ <div piwik-activity-indicator class="loadingManageSuperUser" loading="manageSuperUser.isLoading"></div>
+
+ <div id="superUserAccessUpdated" style="vertical-align:top;"></div>
+
+ <table piwik-content-table id="superUserAccess" >
+ <thead>
+ <tr>
+ <th class='first'>{{ 'UsersManager_User'|translate }}</th>
+ <th>{{ 'UsersManager_Alias'|translate }}</th>
+ <th>{{ 'Installation_SuperUser'|translate }}</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {% if users|length > 1 %}
+ {% for login,alias in usersAliasByLogin if login != 'anonymous' %}
+ <tr>
+ <td id='login'>{{ login }}</td>
+ <td>{{ alias|raw }}</td>
+ <td id='superuser'>
+ {% if login in superUserLogins %}
+ <img src='plugins/UsersManager/images/ok.png' class='accessGranted'
+ ng-click='manageSuperUser.removeSuperUserAccess({{ login|json_encode}})' />
+ {% endif %}
+ {% if not (login in superUserLogins) %}
+ <img src='plugins/UsersManager/images/no-access.png' class='updateAccess'
+ ng-click='manageSuperUser.giveSuperUserAccess({{ login|json_encode }})' />
+ {% endif %}
+ &nbsp;
+ </td>
+ </tr>
+ {% endfor %}
+ {% else %}
<tr>
- <td id='login'>{{ login }}</td>
- <td>{{ alias|raw }}</td>
- <td id='superuser' data-login="{{ login|e('html_attr') }}">
- <img src='plugins/UsersManager/images/ok.png' class='accessGranted' data-hasaccess="1" {% if not (login in superUserLogins) %}style="display:none"{% endif %} />
- <img src='plugins/UsersManager/images/no-access.png' class='updateAccess' data-hasaccess="0" {% if login in superUserLogins %}style="display:none"{% endif %} />
- &nbsp;
+ <td colspan="3">
+ {{ 'UsersManager_NoUsersExist'|translate }}
</td>
</tr>
- {% endfor %}
- {% else %}
- <tr>
- <td colspan="3">
- {{ 'UsersManager_NoUsersExist'|translate }}
- </td>
- </tr>
- {% endif %}
- </tbody>
- </table>
+ {% endif %}
+ </tbody>
+ </table>
- <div id="superUserAccessUpdated" style="vertical-align:top;"></div>
+ <div class="ui-confirm" id="superUserAccessConfirm">
+ <h2> </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="superUserAccessConfirm">
- <h2> </h2>
- <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
- <input role="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
+</div>
{% endif %}
{% endblock %}
diff --git a/plugins/UsersManager/templates/userSettings.twig b/plugins/UsersManager/templates/userSettings.twig
index 48d5f47ed9..2c802670d0 100644
--- a/plugins/UsersManager/templates/userSettings.twig
+++ b/plugins/UsersManager/templates/userSettings.twig
@@ -4,67 +4,62 @@
{% block content %}
-<h2 piwik-enriched-headline>{{ title }}</h2>
-
<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>
-<form id="userSettingsTable">
+<div piwik-content-block content-title="{{ title|e('html_attr') }}" feature="true">
+ <form id="userSettingsTable" piwik-form ng-controller="PersonalSettingsController as personalSettings">
- <div class="form-group">
- <label for="username">{{ 'General_Username'|translate }}</label>
- <div class="form-help">{{ 'UsersManager_YourUsernameCannotBeChanged'|translate }}</div>
- <input value="{{ userLogin }}" id="username" disabled="disabled"/>
- </div>
+ <div piwik-field uicontrol="text" name="username"
+ title="{{ 'General_Username'|translate|e('html_attr') }}"
+ value="{{ userLogin }}" disabled="true"
+ ng-model="personalSettings.username"
+ inline-help="{{ 'UsersManager_YourUsernameCannotBeChanged'|translate|e('html_attr') }}">
+ </div>
- <div class="form-group">
- <label for="alias">{{ 'UsersManager_Alias'|translate }}</label>
- <input value="{{ userAlias }}" id="alias" />
- </div>
+ <div piwik-field uicontrol="text" name="alias"
+ ng-model="personalSettings.alias"
+ title="{{ 'UsersManager_Alias'|translate|e('html_attr') }}"
+ value="{{ userAlias|raw }}">
+ </div>
- <div class="form-group">
- <label for="email">{{ 'UsersManager_Email'|translate }}</label>
- <input value="{{ userEmail }}" id="email"/>
- </div>
+ <div piwik-field uicontrol="text" name="email"
+ ng-model="personalSettings.email"
+ title="{{ 'UsersManager_Email'|translate|e('html_attr') }}"
+ value="{{ userEmail }}">
+ </div>
- <div class="form-group">
- <label for="language">{{ 'General_Language'|translate }}</label>
- <div class="form-help">
+ <div id="languageHelp" class="inline-help-node">
<a href="?module=Proxy&amp;action=redirect&amp;url=http://piwik.org/translations/" target="_blank">
{{ 'LanguagesManager_AboutPiwikTranslations'|translate }}</a>
</div>
- <select name="language" id="language">
- {% for language in languages %}
- <option value="{{ language.code }}" {% if language.code == currentLanguageCode %}selected="selected"{% endif %}
- title="{{ language.name }} ({{ language.english_name }})">{{ language.name }}</option>
- {% endfor %}
- </select>
- </div>
-
- <div class="form-group">
- <label for="timeformat">{{ 'General_TimeFormat'|translate }}</label>
- <select name="timeformat" id="timeformat">
- <option value="1" {% if currentTimeformat == 1 %}selected="selected"{% endif %} title="{{ 'General_12HourClock'|translate }}">{{ 'General_12HourClock'|translate }}</option>
- <option value="0" {% if currentTimeformat == 0 %}selected="selected"{% endif %} title="{{ 'General_24HourClock'|translate }}">{{ 'General_24HourClock'|translate }}</option>
- </select>
- </div>
-
- <div class="form-group">
- <label>{{ 'UsersManager_ReportToLoadByDefault'|translate }}</label>
- <label class="radio">
- <input id="defaultReportRadioAll" type="radio" value="MultiSites"
- name="defaultReport"{% if defaultReport=='MultiSites' %} checked="checked"{% endif %} />
- {{ 'General_AllWebsitesDashboard'|translate }}
- </label>
- <label class="radio">
- <input id="defaultReportSpecific" type="radio" value="1"
- name="defaultReport"{% if defaultReport != 'MultiSites' %} checked="checked"{% endif %} />
- {{ 'General_DashboardForASpecificWebsite'|translate }}
- </label>
+
+ <div piwik-field uicontrol="select" name="language"
+ ng-model="personalSettings.language"
+ title="{{ 'General_Language'|translate|e('html_attr') }}"
+ options="{{ languageOptions|json_encode }}"
+ inline-help="#languageHelp"
+ value="{{ currentLanguageCode }}">
+ </div>
+
+ <div piwik-field uicontrol="select" name="timeformat"
+ ng-model="personalSettings.timeformat"
+ title="{{ 'General_TimeFormat'|translate|e('html_attr') }}"
+ value="{{ currentTimeformat }}" options="{{ timeFormats|json_encode }}">
+ </div>
+
+ <div piwik-field uicontrol="radio" name="defaultReport"
+ ng-model="personalSettings.defaultReport"
+ introduction="{{ 'UsersManager_ReportToLoadByDefault'|translate|e('html_attr') }}"
+ title="{{ 'General_AllWebsitesDashboard'|translate|e('html_attr') }}"
+ value="{{ defaultReport }}" options="{{ defaultReportOptions|json_encode }}">
+ </div>
+
<div piwik-siteselector
+ ng-model="personalSettings.site"
show-selected-site="true"
class="sites_autocomplete"
siteid="{{ defaultReportIdSite }}"
@@ -73,58 +68,47 @@
show-all-sites-item="false"
showselectedsite="true"
id="defaultReportSiteSelector"
- ></div>
- </div>
-
- <div class="form-group">
- <label>{{ 'UsersManager_ReportDateToLoadByDefault'|translate }}</label>
- {% for value,description in availableDefaultDates %}
- <label class="radio">
- <input id="defaultDate-{{ loop.index }}" type="radio"{% if defaultDate==value %} checked="checked"{% endif %} value="{{ value }}" name="defaultDate"/>
- {{ description }}
- </label>
- {% endfor %}
- </div>
-
- {% if isValidHost is defined and isValidHost %}
- <div class="form-group">
- <label for="password">{{ 'General_ChangePassword'|translate }}</label>
- <div class="form-help">
- {{ 'UsersManager_IfYouWouldLikeToChangeThePasswordTypeANewOne'|translate }}
- </div>
- <input value="" autocomplete="off" id="password" type="password"/>
- </div>
- <div class="form-group">
- <div class="form-help">
- {{ 'UsersManager_TypeYourPasswordAgain'|translate }}
- </div>
- <input value="" autocomplete="off" id="passwordBis" type="password"/>
- </div>
- {% endif %}
+ ></div>
- {% if isValidHost is not defined or not isValidHost %}
- <div class="alert alert-danger">
- {{ 'UsersManager_InjectedHostCannotChangePwd'|translate(invalidHost) }}
- {% if not isSuperUser %}{{ 'UsersManager_EmailYourAdministrator'|translate(invalidHostMailLinkStart,'</a>')|raw }}{% endif %}
+ <div piwik-field uicontrol="radio" name="defaultDate"
+ ng-model="personalSettings.defaultDate"
+ introduction="{{ 'UsersManager_ReportDateToLoadByDefault'|translate|e('html_attr') }}"
+ value="{{ defaultDate }}" options="{{ availableDefaultDates|json_encode }}">
</div>
- {% endif %}
- {% import 'ajaxMacros.twig' as ajax %}
- {{ ajax.errorDiv('ajaxErrorUserSettings') }}
- {{ ajax.loadingDiv('ajaxLoadingUserSettings') }}
+ {% if isValidHost is defined and isValidHost %}
- <button type="button" id="userSettingsSubmit">{{ 'General_Save'|translate }}</button>
+ <div piwik-field uicontrol="password" name="password" autocomplete="off"
+ ng-model="personalSettings.password"
+ introduction="{{ 'General_ChangePassword'|translate|e('html_attr') }}"
+ title="{{ 'Login_NewPassword'|translate|e('html_attr') }}"
+ value="" inline-help="{{ 'UsersManager_IfYouWouldLikeToChangeThePasswordTypeANewOne'|translate|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="password" name="passwordBis" autocomplete="off"
+ ng-model="personalSettings.passwordBis"
+ title="{{ 'Login_NewPasswordRepeat'|translate|e('html_attr') }}"
+ value="" inline-help="{{ 'UsersManager_TypeYourPasswordAgain'|translate|e('html_attr') }}">
+ </div>
+ {% endif %}
-</form>
+ {% if isValidHost is not defined or not isValidHost %}
+ <div class="alert alert-danger">
+ {{ 'UsersManager_InjectedHostCannotChangePwd'|translate(invalidHost) }}
+ {% if not isSuperUser %}{{ 'UsersManager_EmailYourAdministrator'|translate(invalidHostMailLinkStart,'</a>')|raw }}{% endif %}
+ </div>
+ {% endif %}
- <hr />
+ <div piwik-save-button onconfirm="personalSettings.save()"
+ saving="personalSettings.loading"></div>
- <h2 piwik-enriched-headline>{{ 'CoreAdminHome_PersonalPluginSettings'|translate }}</h2>
+ </form>
+</div>
- <div piwik-plugin-settings mode="user"></div>
+<div piwik-plugin-settings mode="user"></div>
- <hr />
- <h2 id="excludeCookie">{{ 'UsersManager_ExcludeVisitsViaCookie'|translate }}</h2>
+<div piwik-content-block
+ content-title="{{ 'UsersManager_ExcludeVisitsViaCookie'|translate|e('html_attr') }}">
<p>
{% if ignoreCookieSet %}
{{ 'UsersManager_YourVisitsAreIgnoredOnDomain'|translate("<strong>", piwikHost, "</strong>")|raw }}
@@ -133,8 +117,10 @@
{% endif %}
</p>
<span style="margin-left:20px;">
-<a href='{{ linkTo({'ignoreSalt':ignoreSalt, 'action':'setIgnoreCookie'}) }}#excludeCookie'>&rsaquo; {% if ignoreCookieSet %}{{ 'UsersManager_ClickHereToDeleteTheCookie'|translate }}
- {% else %}{{'UsersManager_ClickHereToSetTheCookieOnDomain'|translate(piwikHost) }}{% endif %}
- <br/>
-</a></span>
+ <a href='{{ linkTo({'ignoreSalt':ignoreSalt, 'action':'setIgnoreCookie'}) }}#excludeCookie'>&rsaquo; {% if ignoreCookieSet %}{{ 'UsersManager_ClickHereToDeleteTheCookie'|translate }}
+ {% else %}{{'UsersManager_ClickHereToSetTheCookieOnDomain'|translate(piwikHost) }}{% endif %}
+ <br/>
+ </a></span>
+</div>
+
{% endblock %}
diff --git a/plugins/VisitorGenerator b/plugins/VisitorGenerator
-Subproject 9e27fa9f5d12c752c1db19b49256ef32fdcbcb5
+Subproject f4bb405511d44420558abe31fcc24f25a2bec42
diff --git a/plugins/VisitsSummary/Reports/Get.php b/plugins/VisitsSummary/Reports/Get.php
index c74a82566f..4c197fc652 100644
--- a/plugins/VisitsSummary/Reports/Get.php
+++ b/plugins/VisitsSummary/Reports/Get.php
@@ -41,7 +41,7 @@ class Get extends \Piwik\Plugin\Report
new ActionsPerVisit(),
new AverageTimeOnSite()
);
- $this->metrics = array(
+ $this->metrics = array(
'nb_uniq_visitors',
'nb_visits',
$this->usersColumn,
diff --git a/plugins/VisitsSummary/templates/_sparklines.twig b/plugins/VisitsSummary/templates/_sparklines.twig
index b00ba537a1..f570b49685 100644
--- a/plugins/VisitsSummary/templates/_sparklines.twig
+++ b/plugins/VisitsSummary/templates/_sparklines.twig
@@ -1,6 +1,6 @@
{% if not isWidget %}
<div class="row">
- <div class="col-md-6">
+ <div class="col m6">
{% endif %}
<div class="sparkline">
@@ -38,7 +38,7 @@
{% if not isWidget %}
</div>
- <div class="col-md-6">
+ <div class="col m6">
{% endif %}
{% if showActionsPluginReports|default(false) %}
diff --git a/plugins/WebsiteMeasurable/MeasurableSettings.php b/plugins/WebsiteMeasurable/MeasurableSettings.php
index 625007238f..7af270b0f9 100644
--- a/plugins/WebsiteMeasurable/MeasurableSettings.php
+++ b/plugins/WebsiteMeasurable/MeasurableSettings.php
@@ -16,6 +16,7 @@ use Piwik\Settings\Setting;
use Piwik\Settings\FieldConfig;
use Piwik\Plugins\SitesManager;
use Exception;
+use Piwik\Url;
/**
* Defines Settings for ExampleSettingsPlugin.
diff --git a/plugins/Widgetize/Widgetize.php b/plugins/Widgetize/Widgetize.php
index 750686c6f9..1181b227a1 100644
--- a/plugins/Widgetize/Widgetize.php
+++ b/plugins/Widgetize/Widgetize.php
@@ -29,11 +29,10 @@ class Widgetize extends \Piwik\Plugin
$jsFiles[] = "libs/jquery/jquery.truncate.js";
$jsFiles[] = "libs/bower_components/jquery.scrollTo/jquery.scrollTo.min.js";
$jsFiles[] = "plugins/Morpheus/javascripts/piwikHelper.js";
- $jsFiles[] = "plugins/Morpheus/javascripts/jquery.icheck.min.js";
- $jsFiles[] = "plugins/Morpheus/javascripts/morpheus.js";
$jsFiles[] = "plugins/CoreHome/javascripts/dataTable.js";
$jsFiles[] = "plugins/Dashboard/javascripts/widgetMenu.js";
- $jsFiles[] = "plugins/Widgetize/javascripts/widgetize.js";
+ $jsFiles[] = "plugins/Widgetize/angularjs/widget-preview/widget-preview.directive.js";
+ $jsFiles[] = "plugins/Widgetize/angularjs/export-widget/export-widget.controller.js";
}
public function getStylesheetFiles(&$stylesheets)
diff --git a/plugins/Widgetize/angularjs/export-widget/export-widget.controller.js b/plugins/Widgetize/angularjs/export-widget/export-widget.controller.js
new file mode 100644
index 0000000000..821fcb5ff7
--- /dev/null
+++ b/plugins/Widgetize/angularjs/export-widget/export-widget.controller.js
@@ -0,0 +1,29 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ExportWidgetController', ExportWidgetController);
+
+ ExportWidgetController.$inject = ['piwik', '$window'];
+
+ function ExportWidgetController(piwik, $window) {
+
+ function getIframeCode(iframeUrl)
+ {
+ var url = iframeUrl.replace(/"/g, '&quot;');
+
+ return '<iframe src="' + url + '" frameborder="0" marginheight="0" marginwidth="0" width="100%" height="100%"></iframe>';
+ }
+
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+ var urlPath = $window.location.protocol + '//' + $window.location.hostname + ($window.location.port == '' ? '' : (':' + $window.location.port)) + $window.location.pathname;
+ this.dashboardUrl = urlPath + '?module=Widgetize&action=iframe&moduleToWidgetize=Dashboard&actionToWidgetize=index&idSite=' + piwik.idSite + '&period=week&date=yesterday';
+ this.dashboardCode = getIframeCode(this.dashboardUrl);
+
+ this.allWebsitesDashboardUrl = urlPath + '?module=Widgetize&action=iframe&moduleToWidgetize=MultiSites&actionToWidgetize=standalone&idSite=' + piwik.idSite + '&period=week&date=yesterday';
+ this.allWebsitesDashboardCode = getIframeCode(this.allWebsitesDashboardUrl);
+ }
+})(); \ No newline at end of file
diff --git a/plugins/Widgetize/angularjs/widget-preview/widget-preview.directive.js b/plugins/Widgetize/angularjs/widget-preview/widget-preview.directive.js
new file mode 100644
index 0000000000..6694b15fae
--- /dev/null
+++ b/plugins/Widgetize/angularjs/widget-preview/widget-preview.directive.js
@@ -0,0 +1,98 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-widget-preview>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidgetPreview', piwikWidgetPreview);
+
+ piwikWidgetPreview.$inject = ['piwik', '$window'];
+
+ function piwikWidgetPreview(piwik, $window){
+
+ function getEmbedUrl(parameters, exportFormat) {
+ var 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 = $window.location.protocol + '//' + $window.location.hostname + ($window.location.port == '' ? '' : (':' + $window.location.port)) + $window.location.pathname + '?';
+ sourceUrl += "module=Widgetize" +
+ "&action=" + exportFormat +
+ "&" + piwik.helper.getQueryStringFromParameters(copyParameters) +
+ "&idSite=" + piwik.idSite +
+ "&period=" + piwik.period +
+ "&date=" + piwik.broadcast.getValueFromUrl('date') +
+ "&disableLink=1&widget=1";
+ return sourceUrl;
+ }
+
+ return {
+ restrict: 'A',
+ controller: function () {
+
+ var self = this;
+
+ this.getInputFormWithHtml = function (inputId, htmlEmbed) {
+ return '<pre piwik-select-on-focus readonly="true" id="' + inputId + '">' + this.htmlentities(htmlEmbed) + '</pre>';
+ };
+
+ this.htmlentities = function (s) {
+ return piwik.helper.escape(piwik.helper.htmlEntities(s));
+ };
+
+ this.callbackAddExportButtonsUnderWidget = function (widgetUniqueId, loadedWidgetElement) {
+ var widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
+ var widgetParameters = widget['parameters'];
+
+ var exportButtonsElement = $('<span id="exportButtons">');
+
+ var urlIframe = 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>' +
+ '<div id="embedThisWidgetIframeInput">' +
+ self.getInputFormWithHtml('iframeEmbed', widgetIframeHtml) +
+ '</div>' +
+ '</div>' +
+ '<div> <label for="embedThisWidgetDirectLink">&rsaquo; Direct Link</label>' +
+ '<div id="embedThisWidgetDirectLink"> ' + self.getInputFormWithHtml('directLinkEmbed', urlIframe) + ' - <a href="' + urlIframe + '" rel="noreferrer" target="_blank">' + _pk_translate('Widgetize_OpenInNewWindow') + '</a></div>'
+ + '</div>'
+ );
+
+ // Finally we append the content to the parent widget DIV
+ $(loadedWidgetElement)
+ .parent()
+ .append(exportButtonsElement);
+
+ piwik.helper.compileAngularComponents(exportButtonsElement);
+ }
+ },
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, controller) {
+ element.widgetPreview({
+ onPreviewLoaded: controller.callbackAddExportButtonsUnderWidget
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/Widgetize/javascripts/widgetize.js b/plugins/Widgetize/javascripts/widgetize.js
deleted file mode 100644
index 3e7d8787b6..0000000000
--- a/plugins/Widgetize/javascripts/widgetize.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-/**
- * @constructor
- */
-function 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) {
- var 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) {
- var widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
- var 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 + '" rel="noreferrer" target="_blank">' + _pk_translate('Widgetize_OpenInNewWindow') + '</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/plugins/Widgetize/stylesheets/widgetize.less b/plugins/Widgetize/stylesheets/widgetize.less
index 26dc31498a..f80de58588 100644
--- a/plugins/Widgetize/stylesheets/widgetize.less
+++ b/plugins/Widgetize/stylesheets/widgetize.less
@@ -28,12 +28,19 @@
padding-bottom: 100px;
}
-#embedThisWidgetIframe,
-#embedThisWidgetFlash,
-#embedThisWidgetEverywhere {
- margin-top: 5px;
+#embedThisWidgetIframe {
+ margin-top: 16px;
}
body>.widget {
margin: 10px 7px;
+ overflow: visible;
+
+ strong {
+ font-weight: 700;
+ }
}
+
+body.widgetized {
+ background-color: @theme-color-widget-exported-background-base;
+} \ No newline at end of file
diff --git a/plugins/Widgetize/templates/iframe.twig b/plugins/Widgetize/templates/iframe.twig
index 66556262b5..f0fc5ef8fa 100644
--- a/plugins/Widgetize/templates/iframe.twig
+++ b/plugins/Widgetize/templates/iframe.twig
@@ -8,13 +8,8 @@
<script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
<![endif]-->
{% include "_jsCssIncludes.twig" %}
- <!--[if IE]>
- <link rel="stylesheet" type="text/css" href="plugins/Morpheus/stylesheets/ieonly.css"/>
- <![endif]-->
</head>
- <!--[if (gte IE 9)|!(IE)]><!-->
- <body ng-app="app">
- <![endif]-->
+ <body ng-app="app" class="widgetized">
<div piwik-popover-handler></div>
<div class="widget">
{{ content|raw }}
diff --git a/plugins/Widgetize/templates/index.twig b/plugins/Widgetize/templates/index.twig
index 19569aed92..455127ff57 100644
--- a/plugins/Widgetize/templates/index.twig
+++ b/plugins/Widgetize/templates/index.twig
@@ -11,33 +11,12 @@
<div>
- <script type="text/javascript">
- $(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
- });
- });
-</script>
-
-<h2 piwik-enriched-headline>{{ title }}</h2>
-
-<div class="widgetize">
- <p>With Piwik, you can export your Web Analytics reports on your blog, website, or intranet dashboard... in one click.
-
- <h2 class="secondary">Authentication</h2>
+<div class="widgetize" ng-controller="ExportWidgetController as exportWidget">
+<div piwik-content-intro>
+ <h2 piwik-enriched-headline>{{ title|e('html_attr') }}</h2>
+ <p>With Piwik, you can export your Web Analytics reports on your blog, website, or intranet dashboard... in one click.</p>
+</div>
+<div piwik-content-block content-title="Authentication">
<p>
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' rel='noreferrer' target='_blank'>Users Management section</a>.
@@ -45,25 +24,36 @@
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='{{ linkTo({'module':'API','action':'listAllAPI'}) }}' rel='noreferrer' target='_blank'>API page</a>) in the widget URL.
</p>
-
- <h2>Widgetize dashboards</h2>
+</div>
+<div piwik-content-block content-title="Widgetize dashboards">
<p>You can also display the full Piwik dashboard in your application or website in an IFRAME
- (<a href='' rel='noreferrer' target='_blank' id='linkDashboardUrl'>see example</a>).
+ (<a ng-href='{{ '{{ exportWidget.dashboardUrl }}'|raw }}' rel='noreferrer' target='_blank' >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>
+ For example, for idSite=1 and date=yesterday, you can write:
+
<br />
- You can also widgetize the all websites dashboard in an IFRAME (<a href='' rel='noreferrer' target='_blank' id='linkAllWebsitesDashboardUrl'>see example</a>)
- <span id='exportAllWebsitesDashboard'></span>
- </p>
- <h2>Widgetize reports</h2>
+ <pre piwik-select-on-focus ng-bind="exportWidget.dashboardCode"> </pre>
+
+ <br />
+ You can also widgetize the all websites dashboard in an IFRAME
+ (<a ng-href='{{ '{{ exportWidget.allWebsitesDashboardUrl }}'|raw }}' rel='noreferrer' target='_blank' id='linkAllWebsitesDashboardUrl'>see example</a>)
+
+ <br />
+
+ <pre piwik-select-on-focus ng-bind="exportWidget.allWebsitesDashboardCode"> </pre>
+ </p>
+</div>
+<div piwik-content-block content-title="Widgetize reports">
<p>Select a report, and copy paste in your page the embed code below the widget:
- <div id="widgetPreview"></div>
+ <div piwik-widget-preview></div>
- <div id='iframeDivToExport' style='display:none;'></div>
+ <br class="clearfix" />
+
+</div>
</div>
</div>
diff --git a/plugins/Widgetize/tests/System/WidgetTest.php b/plugins/Widgetize/tests/System/WidgetTest.php
index 72861e6faf..40ad26252f 100644
--- a/plugins/Widgetize/tests/System/WidgetTest.php
+++ b/plugins/Widgetize/tests/System/WidgetTest.php
@@ -357,10 +357,9 @@ class WidgetTest extends SystemTestCase
),
),array (
'name' => 'Browser engines',
- 'uniqueId' => 'widgetDevicesDetectiongetBrowserEnginesviewDataTablegraphPie',
+ 'uniqueId' => 'widgetDevicesDetectiongetBrowserEngines',
'parameters' =>
array (
- 'viewDataTable' => 'graphPie',
'module' => 'DevicesDetection',
'action' => 'getBrowserEngines',
),
@@ -414,19 +413,17 @@ class WidgetTest extends SystemTestCase
),
),array (
'name' => 'Visits per visit duration',
- 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDurationviewDataTablecloud',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDuration',
'parameters' =>
array (
- 'viewDataTable' => 'cloud',
'module' => 'VisitorInterest',
'action' => 'getNumberOfVisitsPerVisitDuration',
),
),array (
'name' => 'Visits per number of pages',
- 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPageviewDataTablecloud',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPage',
'parameters' =>
array (
- 'viewDataTable' => 'cloud',
'module' => 'VisitorInterest',
'action' => 'getNumberOfVisitsPerPage',
),
@@ -468,28 +465,25 @@ class WidgetTest extends SystemTestCase
),
),array (
'name' => 'Visits per local time',
- 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTimeviewDataTablegraphVerticalBar',
+ 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTime',
'parameters' =>
array (
- 'viewDataTable' => 'graphVerticalBar',
'module' => 'VisitTime',
'action' => 'getVisitInformationPerLocalTime',
),
),array (
'name' => 'Visits per server time',
- 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTimeviewDataTablegraphVerticalBar',
+ 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTime',
'parameters' =>
array (
- 'viewDataTable' => 'graphVerticalBar',
'module' => 'VisitTime',
'action' => 'getVisitInformationPerServerTime',
),
),array (
'name' => 'Visits by Day of Week',
- 'uniqueId' => 'widgetVisitTimegetByDayOfWeekviewDataTablegraphVerticalBar',
+ 'uniqueId' => 'widgetVisitTimegetByDayOfWeek',
'parameters' =>
array (
- 'viewDataTable' => 'graphVerticalBar',
'module' => 'VisitTime',
'action' => 'getByDayOfWeek',
),
@@ -599,19 +593,17 @@ class WidgetTest extends SystemTestCase
),
),array (
'name' => 'Referrer Types',
- 'uniqueId' => 'widgetReferrersgetReferrerTypeviewDataTabletableAllColumns',
+ 'uniqueId' => 'widgetReferrersgetReferrerType',
'parameters' =>
array (
- 'viewDataTable' => 'tableAllColumns',
'module' => 'Referrers',
'action' => 'getReferrerType',
),
),array (
'name' => 'Referrers',
- 'uniqueId' => 'widgetReferrersgetAllviewDataTabletableAllColumns',
+ 'uniqueId' => 'widgetReferrersgetAll',
'parameters' =>
array (
- 'viewDataTable' => 'tableAllColumns',
'module' => 'Referrers',
'action' => 'getAll',
),
@@ -641,10 +633,9 @@ class WidgetTest extends SystemTestCase
),
),array (
'name' => 'Social Networks',
- 'uniqueId' => 'widgetReferrersgetSocialsviewDataTablegraphPie',
+ 'uniqueId' => 'widgetReferrersgetSocials',
'parameters' =>
array (
- 'viewDataTable' => 'graphPie',
'module' => 'Referrers',
'action' => 'getSocials',
),
@@ -765,10 +756,9 @@ class WidgetTest extends SystemTestCase
),
),array (
'name' => 'Pie graph',
- 'uniqueId' => 'widgetExampleUIgetPlanetRatiosviewDataTablegraphPie',
+ 'uniqueId' => 'widgetExampleUIgetPlanetRatios',
'parameters' =>
array (
- 'viewDataTable' => 'graphPie',
'module' => 'ExampleUI',
'action' => 'getPlanetRatios',
),
@@ -784,10 +774,9 @@ class WidgetTest extends SystemTestCase
),
),array (
'name' => 'Advanced tag cloud: with logos and links',
- 'uniqueId' => 'widgetExampleUIgetPlanetRatiosWithLogosviewDataTablecloud',
+ 'uniqueId' => 'widgetExampleUIgetPlanetRatiosWithLogos',
'parameters' =>
array (
- 'viewDataTable' => 'cloud',
'module' => 'ExampleUI',
'action' => 'getPlanetRatiosWithLogos',
)
@@ -995,6 +984,30 @@ class WidgetTest extends SystemTestCase
'module' => 'Goals',
'action' => 'getItemsCategory',
),
+ ), array (
+ 'name' => 'Latest Piwik Plugin Updates',
+ 'uniqueId' => 'widgetCorePluginsAdmingetNewPlugins',
+ 'parameters' =>
+ array (
+ 'module' => 'CorePluginsAdmin',
+ 'action' => 'getNewPlugins',
+ ),
+ ), array (
+ 'name' => 'System Check',
+ 'uniqueId' => 'widgetInstallationgetSystemCheck',
+ 'parameters' =>
+ array (
+ 'module' => 'Installation',
+ 'action' => 'getSystemCheck',
+ ),
+ ), array (
+ 'name' => 'System Summary',
+ 'uniqueId' => 'widgetCoreHomegetSystemSummary',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'getSystemSummary',
+ ),
),
);
}