diff --git a/plugins/API/API.php b/plugins/API/API.php
index 4f254931036204835b95567e6cd09f122ac6222b..5c95297a1ef90f00dd48995f0ddf7b461401a0c4 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -15,6 +15,7 @@ use Piwik\DataTable;
 use Piwik\DataTable\Filter\ColumnDelete;
 use Piwik\DataTable\Row;
 use Piwik\Date;
+use Piwik\IP;
 use Piwik\Menu\MenuTop;
 use Piwik\Metrics;
 use Piwik\Period;
@@ -58,6 +59,18 @@ class API extends \Piwik\Plugin\API
         return Version::VERSION;
     }
 
+    /**
+     * Returns the most accurate IP address availble for the current user, in
+     * IPv4 format. This could be the proxy client's IP address.
+     *
+     * @return string IP address in presentation format.
+     */
+    public function getIpFromHeader()
+    {
+        Piwik::checkUserHasSomeViewAccess();
+        return IP::getIpFromHeader();
+    }
+
     /**
      * Returns the section [APISettings] if defined in config.ini.php
      * @return array
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index d0e9672fb83c9ea3f6d9d5317c0a2b90a8a8d30e..d651c8f1ca6b592c4ffc8855c8a9cd3b6a6a7d96 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -208,4 +208,15 @@ class API extends \Piwik\Plugin\API
         return array();
     }
 
+    /**
+     * Return true if plugin is activated, false otherwise
+     *
+     * @param string $pluginName
+     * @return bool
+     */
+    public function isPluginActivated($pluginName)
+    {
+        Piwik::checkUserHasSomeViewAccess();
+        return \Piwik\Plugin\Manager::getInstance()->isPluginActivated($pluginName);
+    }
 }
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index cd4741f83e4e99073ec12905356e66489270db39..18ad9f68c286c0d7f07229ec18408758a8af9102 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -99,6 +99,7 @@ class CoreHome extends \Piwik\Plugin
         $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/evolution.js";
         $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/length.js";
         $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/trim.js";
+        $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/pretty-url.js";
 
         $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.js";
         $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js";
@@ -196,5 +197,16 @@ class CoreHome extends \Piwik\Plugin
         $translationKeys[] = 'General_Search';
         $translationKeys[] = 'General_MoreDetails';
         $translationKeys[] = 'General_Help';
+        $translationKeys[] = 'General_MoreDetails';
+        $translationKeys[] = 'General_Help';
+        $translationKeys[] = 'General_Id';
+        $translationKeys[] = 'General_Name';
+        $translationKeys[] = 'General_JsTrackingTag';
+        $translationKeys[] = 'General_Yes';
+        $translationKeys[] = 'General_No';
+        $translationKeys[] = 'General_Edit';
+        $translationKeys[] = 'General_Delete';
+        $translationKeys[] = 'General_Default';
+        $translationKeys[] = 'General_LoadingData';
     }
 }
diff --git a/plugins/CoreHome/angularjs/common/directives/dialog.js b/plugins/CoreHome/angularjs/common/directives/dialog.js
index 86762db83f4b3b624ad880a9e9344a93882eece5..71157aeb712395d0ef05ba59538ca457196ebbc5 100644
--- a/plugins/CoreHome/angularjs/common/directives/dialog.js
+++ b/plugins/CoreHome/angularjs/common/directives/dialog.js
@@ -15,7 +15,7 @@
  * </div>
  * Will execute the "executeMyFunction" function in the current scope once the yes button is pressed.
  */
-angular.module('piwikApp.directive').directive('piwikDialog', function(piwik) {
+angular.module('piwikApp.directive').directive('piwikDialog', function(piwik, $parse) {
 
     return {
         restrict: 'A',
@@ -24,7 +24,7 @@ angular.module('piwikApp.directive').directive('piwikDialog', function(piwik) {
             element.css('display', 'none');
 
             element.on( "dialogclose", function() {
-                scope.$eval(attrs.piwikDialog+'=false');
+                scope.$apply($parse(attrs.piwikDialog).assign(scope, false));
             });
 
             scope.$watch(attrs.piwikDialog, function(newValue, oldValue) {
diff --git a/plugins/CoreHome/angularjs/common/filters/pretty-url.js b/plugins/CoreHome/angularjs/common/filters/pretty-url.js
new file mode 100644
index 0000000000000000000000000000000000000000..f46a64bfaf2fbdf50173d185e6ee347301660228
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/filters/pretty-url.js
@@ -0,0 +1,12 @@
+/*!
+ * Piwik - Web Analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+angular.module('piwikApp.filter').filter('prettyUrl', function() {
+    return function(input) {
+        return input.trim().replace('http://', '');
+    };
+});
diff --git a/plugins/CoreHome/angularjs/common/services/piwik-api.js b/plugins/CoreHome/angularjs/common/services/piwik-api.js
index 31a7c60085ac5c07143e6db2d9a5bf73928c64d6..18a635306e368a8e8e0c53741c6f62ea7d1d59b5 100644
--- a/plugins/CoreHome/angularjs/common/services/piwik-api.js
+++ b/plugins/CoreHome/angularjs/common/services/piwik-api.js
@@ -162,7 +162,7 @@ angular.module('piwikApp.service').factory('piwikApi', function ($http, $q, $roo
      */
     piwikApi.fetch = function (getParams) {
 
-        getParams.module = 'API';
+        getParams.module = getParams.module || 'API';
         getParams.format = 'JSON';
 
         addParams(getParams, 'GET');
diff --git a/plugins/Feedback/Feedback.php b/plugins/Feedback/Feedback.php
index 6f56fdc2a45d2894cf16d00e3eff973eb9f04a25..329f4cccc146ddfa760065ec980cc6bc214518b6 100644
--- a/plugins/Feedback/Feedback.php
+++ b/plugins/Feedback/Feedback.php
@@ -51,5 +51,6 @@ class Feedback extends \Piwik\Plugin
         $translationKeys[] = 'Feedback_SendFeedback';
         $translationKeys[] = 'Feedback_RateFeatureSendFeedbackInformation';
         $translationKeys[] = 'General_Ok';
+        $translationKeys[] = 'General_Cancel';
     }
 }
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index fea8fb0b183904f7c2e98041ac2318a4019129c0..b3b26e5230239c43e73881f669ba62473005b2af 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -611,5 +611,9 @@ class Goals extends \Piwik\Plugin
         $translationKeys[] = 'Goals_AddGoal';
         $translationKeys[] = 'Goals_UpdateGoal';
         $translationKeys[] = 'Goals_DeleteGoalConfirm';
+        $translationKeys[] = 'Goals_UpdateGoal';
+        $translationKeys[] = 'Goals_DeleteGoalConfirm';
+        $translationKeys[] = 'Goals_Ecommerce';
+        $translationKeys[] = 'Goals_Optional';
     }
 }
diff --git a/plugins/Morpheus/stylesheets/theme.less b/plugins/Morpheus/stylesheets/theme.less
index d0e9345c1063abc01615728e4e4e5c845554a6ec..582d3c4d5852949d515df0828f7c5f07ba637dfc 100644
--- a/plugins/Morpheus/stylesheets/theme.less
+++ b/plugins/Morpheus/stylesheets/theme.less
@@ -877,13 +877,6 @@ table#users {
     }
 }
 
-table#editSites {
-    .editSite:hover > span,
-    .deleteSite:hover > span {
-        text-decoration: underline;
-    }
-}
-
 // previous style overrides
 #header_message a {
     text-decoration: underline;
diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php
index 1f43f22a3a8851ab1e62f5223dbe0f45841e8295..c44a269b9120f36f07152eadbcb01ce87c71f270 100644
--- a/plugins/SitesManager/API.php
+++ b/plugins/SitesManager/API.php
@@ -311,12 +311,19 @@ class API extends \Piwik\Plugin\API
      * Returns the list of websites with the 'admin' access for the current user.
      * For the superUser it returns all the websites in the database.
      *
+     * @param bool $fetchAliasUrls
      * @return array for each site, an array of information (idsite, name, main_url, etc.)
      */
-    public function getSitesWithAdminAccess()
+    public function getSitesWithAdminAccess($fetchAliasUrls = false)
     {
         $sitesId = $this->getSitesIdWithAdminAccess();
-        return $this->getSitesFromIds($sitesId);
+        $sites = $this->getSitesFromIds($sitesId);
+
+        if ($fetchAliasUrls)
+            foreach ($sites as &$site)
+                $site['alias_urls'] = API::getInstance()->getSiteUrlsFromId($site['idsite']);
+
+        return $sites;
     }
 
     /**
@@ -1240,6 +1247,17 @@ class API extends \Piwik\Plugin\API
         }, $currencies);
     }
 
+    /**
+     * Return true if Timezone support is enabled on server
+     *
+     * @return bool
+     */
+    public function isTimezoneSupportEnabled()
+    {
+        Piwik::checkUserHasSomeViewAccess();
+        return SettingsServer::isTimezoneSupportEnabled();
+    }
+
     /**
      * Returns the list of timezones supported.
      * Used for addSite and updateSite
diff --git a/plugins/SitesManager/Controller.php b/plugins/SitesManager/Controller.php
index d17232c2db9bf1075071168e61e52d5cc8a49754..34fe1cc2a3d1b1eec702f3bd07b856e3ba82b4c0 100644
--- a/plugins/SitesManager/Controller.php
+++ b/plugins/SitesManager/Controller.php
@@ -31,49 +31,30 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
     {
         $view = new View('@SitesManager/index');
 
-        Site::clearCache();
-        $sites = API::getInstance()->getSitesWithAdminAccess();
-
-        foreach ($sites as &$site) {
-            $site['alias_urls'] = API::getInstance()->getSiteUrlsFromId($site['idsite']);
-            $site['excluded_ips'] = explode(',', $site['excluded_ips']);
-            $site['excluded_parameters'] = explode(',', $site['excluded_parameters']);
-            $site['excluded_user_agents'] = explode(',', $site['excluded_user_agents']);
-        }
-        $view->adminSites = $sites;
-        $view->adminSitesCount = count($sites);
-
-        $timezones = API::getInstance()->getTimezonesList();
-        $view->timezoneSupported = SettingsServer::isTimezoneSupportEnabled();
-        $view->timezones = Common::json_encode($timezones);
-        $view->defaultTimezone = API::getInstance()->getDefaultTimezone();
-
-        $view->currencies = Common::json_encode(API::getInstance()->getCurrencyList());
-        $view->defaultCurrency = API::getInstance()->getDefaultCurrency();
+        $this->setBasicVariablesView($view);
 
-        $view->utcTime = Date::now()->getDatetime();
-        $excludedIpsGlobal = API::getInstance()->getExcludedIpsGlobal();
-        $view->globalExcludedIps = str_replace(',', "\n", $excludedIpsGlobal);
-        $excludedQueryParametersGlobal = API::getInstance()->getExcludedQueryParametersGlobal();
-        $view->globalExcludedQueryParameters = str_replace(',', "\n", $excludedQueryParametersGlobal);
+        return $view->render();
+    }
 
-        $globalExcludedUserAgents = API::getInstance()->getExcludedUserAgentsGlobal();
-        $view->globalExcludedUserAgents = str_replace(',', "\n", $globalExcludedUserAgents);
+    public function getGlobalSettings() {
 
-        $view->globalSearchKeywordParameters = API::getInstance()->getSearchKeywordParametersGlobal();
-        $view->globalSearchCategoryParameters = API::getInstance()->getSearchCategoryParametersGlobal();
-        $view->isSearchCategoryTrackingEnabled = \Piwik\Plugin\Manager::getInstance()->isPluginActivated('CustomVariables');
-        $view->allowSiteSpecificUserAgentExclude =
-            API::getInstance()->isSiteSpecificUserAgentExcludeEnabled();
+        Piwik::checkUserHasSomeViewAccess();
 
-        $view->globalKeepURLFragments = API::getInstance()->getKeepURLFragmentsGlobal();
+        $response = new ResponseBuilder(Common::getRequestVar('format'));
 
-        $view->currentIpAddress = IP::getIpFromHeader();
+        $globalSettings = array();
 
-        $view->showAddSite = (boolean)Common::getRequestVar('showaddsite', false);
+        $globalSettings['keepURLFragmentsGlobal'] = API::getInstance()->getKeepURLFragmentsGlobal();
+        $globalSettings['siteSpecificUserAgentExcludeEnabled'] = API::getInstance()->isSiteSpecificUserAgentExcludeEnabled();
+        $globalSettings['defaultCurrency'] = API::getInstance()->getDefaultCurrency();
+        $globalSettings['searchKeywordParametersGlobal'] = API::getInstance()->getSearchKeywordParametersGlobal();
+        $globalSettings['searchCategoryParametersGlobal'] = API::getInstance()->getSearchCategoryParametersGlobal();
+        $globalSettings['defaultTimezone'] = API::getInstance()->getDefaultTimezone();
+        $globalSettings['excludedIpsGlobal'] = API::getInstance()->getExcludedIpsGlobal();
+        $globalSettings['excludedQueryParametersGlobal'] = API::getInstance()->getExcludedQueryParametersGlobal();
+        $globalSettings['excludedUserAgentsGlobal'] = API::getInstance()->getExcludedUserAgentsGlobal();
 
-        $this->setBasicVariablesView($view);
-        return $view->render();
+        return $response->getResponse($globalSettings);
     }
 
     /**
diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php
index a5107cc051b7f67ad6b94699228d621a06c583fe..edd72bdb4cc23f51282b2d42d183b13321213442 100644
--- a/plugins/SitesManager/SitesManager.php
+++ b/plugins/SitesManager/SitesManager.php
@@ -44,7 +44,10 @@ class SitesManager extends \Piwik\Plugin
      */
     public function getJsFiles(&$jsFiles)
     {
-        $jsFiles[] = "plugins/SitesManager/javascripts/SitesManager.js";
+        $jsFiles[] = "plugins/SitesManager/javascripts/sites-manager-recipes.js";
+        $jsFiles[] = "plugins/SitesManager/javascripts/sites-manager-directives.js";
+        $jsFiles[] = "plugins/SitesManager/javascripts/sites-manager-controller.js";
+        $jsFiles[] = "plugins/SitesManager/javascripts/sites-manager-site-controller.js";
     }
 
     /**
@@ -200,5 +203,61 @@ class SitesManager extends \Piwik\Plugin
         $translationKeys[] = "General_OrCancel";
         $translationKeys[] = "SitesManager_OnlyOneSiteAtTime";
         $translationKeys[] = "SitesManager_DeleteConfirm";
+        $translationKeys[] = "SitesManager_Urls";
+        $translationKeys[] = "SitesManager_ExcludedIps";
+        $translationKeys[] = "SitesManager_ExcludedParameters";
+        $translationKeys[] = "SitesManager_ExcludedUserAgents";
+        $translationKeys[] = "SitesManager_Timezone";
+        $translationKeys[] = "SitesManager_Currency";
+        $translationKeys[] = "SitesManager_ShowTrackingTag";
+        $translationKeys[] = "SitesManager_AliasUrlHelp";
+        $translationKeys[] = "SitesManager_KeepURLFragmentsLong";
+        $translationKeys[] = "SitesManager_HelpExcludedIps";
+        $translationKeys[] = "SitesManager_ListOfQueryParametersToExclude";
+        $translationKeys[] = "SitesManager_PiwikWillAutomaticallyExcludeCommonSessionParameters";
+        $translationKeys[] = "SitesManager_GlobalExcludedUserAgentHelp1";
+        $translationKeys[] = "SitesManager_GlobalListExcludedUserAgents_Desc";
+        $translationKeys[] = "SitesManager_GlobalExcludedUserAgentHelp2";
+        $translationKeys[] = "SitesManager_WebsitesManagement";
+        $translationKeys[] = "SitesManager_MainDescription";
+        $translationKeys[] = "SitesManager_YouCurrentlyHaveAccessToNWebsites";
+        $translationKeys[] = "SitesManager_SuperUserAccessCan";
+        $translationKeys[] = "SitesManager_EnableSiteSearch";
+        $translationKeys[] = "SitesManager_DisableSiteSearch";
+        $translationKeys[] = "SitesManager_SearchUseDefault";
+        $translationKeys[] = "SitesManager_SiteSearchUse";
+        $translationKeys[] = "SitesManager_SearchKeywordLabel";
+        $translationKeys[] = "SitesManager_SearchCategoryLabel";
+        $translationKeys[] = "SitesManager_YourCurrentIpAddressIs";
+        $translationKeys[] = "SitesManager_SearchKeywordParametersDesc";
+        $translationKeys[] = "SitesManager_SearchCategoryParametersDesc";
+        $translationKeys[] = "SitesManager_CurrencySymbolWillBeUsedForGoals";
+        $translationKeys[] = "SitesManager_ChangingYourTimezoneWillOnlyAffectDataForward";
+        $translationKeys[] = "SitesManager_AdvancedTimezoneSupportNotFound";
+        $translationKeys[] = "SitesManager_ChooseCityInSameTimezoneAsYou";
+        $translationKeys[] = "SitesManager_UTCTimeIs";
+        $translationKeys[] = "SitesManager_EnableEcommerce";
+        $translationKeys[] = "SitesManager_NotAnEcommerceSite";
+        $translationKeys[] = "SitesManager_EcommerceHelp";
+        $translationKeys[] = "SitesManager_PiwikOffersEcommerceAnalytics";
+        $translationKeys[] = "SitesManager_GlobalWebsitesSettings";
+        $translationKeys[] = "SitesManager_GlobalListExcludedIps";
+        $translationKeys[] = "SitesManager_ListOfIpsToBeExcludedOnAllWebsites";
+        $translationKeys[] = "SitesManager_GlobalListExcludedQueryParameters";
+        $translationKeys[] = "SitesManager_ListOfQueryParametersToBeExcludedOnAllWebsites";
+        $translationKeys[] = "SitesManager_GlobalListExcludedUserAgents";
+        $translationKeys[] = "SitesManager_EnableSiteSpecificUserAgentExclude_Help";
+        $translationKeys[] = "SitesManager_EnableSiteSpecificUserAgentExclude";
+        $translationKeys[] = "SitesManager_KeepURLFragments";
+        $translationKeys[] = "SitesManager_KeepURLFragmentsHelp";
+        $translationKeys[] = "SitesManager_KeepURLFragmentsHelp2";
+        $translationKeys[] = "SitesManager_TrackingSiteSearch";
+        $translationKeys[] = "SitesManager_SearchParametersNote";
+        $translationKeys[] = "SitesManager_SearchParametersNote2";
+        $translationKeys[] = "SitesManager_SearchCategoryDesc";
+        $translationKeys[] = "SitesManager_DefaultTimezoneForNewWebsites";
+        $translationKeys[] = "SitesManager_SelectDefaultTimezone";
+        $translationKeys[] = "SitesManager_DefaultCurrencyForNewWebsites";
+        $translationKeys[] = "SitesManager_SelectDefaultCurrency";
     }
 }
diff --git a/plugins/SitesManager/javascripts/SitesManager.js b/plugins/SitesManager/javascripts/SitesManager.js
deleted file mode 100644
index b8ad4c325d177dbc8bc1e6aee11248321086003c..0000000000000000000000000000000000000000
--- a/plugins/SitesManager/javascripts/SitesManager.js
+++ /dev/null
@@ -1,473 +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
- */
-
-// NOTE: if you cannot find the definition of a variable here, look in index.twig
-function SitesManager(_timezones, _currencies, _defaultTimezone, _defaultCurrency) {
-
-    var timezones = _timezones;
-    var currencies = _currencies;
-    var defaultTimezone = _defaultTimezone;
-    var defaultCurrency = _defaultCurrency;
-    var siteBeingEdited = false;
-    var siteBeingEditedName = '';
-
-    function sendDeleteSiteAJAX(idSite) {
-        var ajaxHandler = new ajaxHelper();
-        ajaxHandler.addParams({
-            idSite: idSite,
-            module: 'API',
-            format: 'json',
-            method: 'SitesManager.deleteSite'
-        }, 'GET');
-        ajaxHandler.redirectOnSuccess();
-        ajaxHandler.setLoadingElement();
-        ajaxHandler.send(true);
-    }
-
-    function sendAddSiteAJAX(row) {
-        var siteName = $(row).find('input#name').val();
-        var urls = $(row).find('textarea#urls').val();
-        urls = urls.trim().split("\n");
-        var excludedIps = $(row).find('textarea#excludedIps').val();
-        excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
-        var timezone = $(row).find('#timezones option:selected').val();
-        var currency = $(row).find('#currencies option:selected').val();
-        var excludedQueryParameters = $(row).find('textarea#excludedQueryParameters').val();
-        excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
-        var excludedUserAgents = $(row).find('textarea#excludedUserAgents').val();
-        excludedUserAgents = piwikHelper.getApiFormatTextarea(excludedUserAgents);
-        var keepURLFragments = $('#keepURLFragmentSelect', row).val();
-        var ecommerce = $(row).find('#ecommerce option:selected').val();
-        var sitesearch = $(row).find('#sitesearch option:selected').val();
-        var searchKeywordParameters = $('input#searchKeywordParameters').val();
-        var searchCategoryParameters = $('input#searchCategoryParameters').val();
-
-        var ajaxHandler = new ajaxHelper();
-        ajaxHandler.addParams({
-            module: 'API',
-            format: 'json',
-            method: 'SitesManager.addSite'
-        }, 'GET');
-        ajaxHandler.addParams({
-            siteName: siteName,
-            timezone: timezone,
-            currency: currency,
-            ecommerce: ecommerce,
-            excludedIps: excludedIps,
-            excludedQueryParameters: excludedQueryParameters,
-            excludedUserAgents: excludedUserAgents,
-            keepURLFragments: keepURLFragments,
-            siteSearch: sitesearch,
-            searchKeywordParameters: searchKeywordParameters,
-            searchCategoryParameters: searchCategoryParameters,
-            urls: urls
-        }, 'POST');
-        ajaxHandler.redirectOnSuccess();
-        ajaxHandler.setLoadingElement();
-        ajaxHandler.send(true);
-    }
-
-    function sendUpdateSiteAJAX(row) {
-        var siteName = $(row).find('input#siteName').val();
-        var idSite = $(row).children('#idSite').html();
-        var urls = $(row).find('textarea#urls').val();
-        urls = urls.trim().split("\n");
-        var excludedIps = $(row).find('textarea#excludedIps').val();
-        excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
-
-        var excludedQueryParameters = $(row).find('textarea#excludedQueryParameters').val();
-        excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
-        var excludedUserAgents = $(row).find('textarea#excludedUserAgents').val();
-        excludedUserAgents = piwikHelper.getApiFormatTextarea(excludedUserAgents);
-        var keepURLFragments = $('#keepURLFragmentSelect', row).val();
-        var timezone = $(row).find('#timezones option:selected').val();
-        var currency = $(row).find('#currencies option:selected').val();
-        var ecommerce = $(row).find('#ecommerce option:selected').val();
-        var sitesearch = $(row).find('#sitesearch option:selected').val();
-        var searchKeywordParameters = $('input#searchKeywordParameters').val();
-        var searchCategoryParameters = $('input#searchCategoryParameters').val();
-
-        var ajaxHandler = new ajaxHelper();
-        ajaxHandler.addParams({
-            module: 'API',
-            format: 'json',
-            method: 'SitesManager.updateSite',
-            idSite: idSite
-        }, 'GET');
-        ajaxHandler.addParams({
-            siteName: siteName,
-            timezone: timezone,
-            currency: currency,
-            ecommerce: ecommerce,
-            excludedIps: excludedIps,
-            excludedQueryParameters: excludedQueryParameters,
-            excludedUserAgents: excludedUserAgents,
-            keepURLFragments: keepURLFragments,
-            siteSearch: sitesearch,
-            searchKeywordParameters: searchKeywordParameters,
-            searchCategoryParameters: searchCategoryParameters,
-            urls: urls
-        }, 'POST');
-        ajaxHandler.redirectOnSuccess();
-        ajaxHandler.setLoadingElement();
-        ajaxHandler.send(true);
-    }
-
-    function sendGlobalSettingsAJAX() {
-        var timezone = $('#defaultTimezone').find('option:selected').val();
-        var currency = $('#defaultCurrency').find('option:selected').val();
-        var excludedIps = $('textarea#globalExcludedIps').val();
-        excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
-        var excludedQueryParameters = $('textarea#globalExcludedQueryParameters').val();
-        excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
-        var globalExcludedUserAgents = $('textarea#globalExcludedUserAgents').val();
-        globalExcludedUserAgents = piwikHelper.getApiFormatTextarea(globalExcludedUserAgents);
-        var globalKeepURLFragments = $('#globalKeepURLFragments').is(':checked') ? 1 : 0;
-        var searchKeywordParameters = $('input#globalSearchKeywordParameters').val();
-        var searchCategoryParameters = $('input#globalSearchCategoryParameters').val();
-        var enableSiteUserAgentExclude = $('input#enableSiteUserAgentExclude').is(':checked') ? 1 : 0;
-
-        var ajaxHandler = new ajaxHelper();
-        ajaxHandler.addParams({
-            module: 'SitesManager',
-            format: 'json',
-            action: 'setGlobalSettings'
-        }, 'GET');
-        ajaxHandler.addParams({
-            timezone: timezone,
-            currency: currency,
-            excludedIps: excludedIps,
-            excludedQueryParameters: excludedQueryParameters,
-            excludedUserAgents: globalExcludedUserAgents,
-            keepURLFragments: globalKeepURLFragments,
-            enableSiteUserAgentExclude: enableSiteUserAgentExclude,
-            searchKeywordParameters: searchKeywordParameters,
-            searchCategoryParameters: searchCategoryParameters
-        }, 'POST');
-        ajaxHandler.redirectOnSuccess();
-        ajaxHandler.setLoadingElement('#ajaxLoadingGlobalSettings');
-        ajaxHandler.setErrorElement('#ajaxErrorGlobalSettings');
-        ajaxHandler.send(true);
-    }
-
-    this.init = function () {
-        $('.addRowSite').click(function () {
-            piwikHelper.hideAjaxError();
-            $('.addRowSite').toggle();
-            
-            var excludedUserAgentCell = '';
-            if ($('#exclude-user-agent-header').is(':visible')) {
-                excludedUserAgentCell = '<td><textarea cols="20" rows="4" id="excludedUserAgents"></textarea><br />' + excludedUserAgentsHelp + '</td>';
-            }
-
-            var numberOfRows = $('table#editSites')[0].rows.length;
-            var newRowId = 'rowNew' + numberOfRows;
-            var submitButtonHtml = '<input type="submit" class="addsite submit" value="' + _pk_translate('General_Save') + '" />';
-            $($.parseHTML(' <tr id="' + newRowId + '">\
-				<td>&nbsp;</td>\
-				<td><input id="name" value="Name" size="15" /><br/><br/><br/>' + submitButtonHtml + '</td>\
-				<td><textarea cols="25" rows="3" id="urls">http://siteUrl.com/\nhttp://siteUrl2.com/</textarea><br />' + aliasUrlsHelp + keepURLFragmentSelectHTML + '</td>\
-				<td><textarea cols="20" rows="4" id="excludedIps"></textarea><br />' + excludedIpHelp + '</td>\
-				<td><textarea cols="20" rows="4" id="excludedQueryParameters"></textarea><br />' + excludedQueryParametersHelp + '</td>' +
-				excludedUserAgentCell +
-				'<td>' + getSitesearchSelector(false) + '</td>\
-				<td>' + getTimezoneSelector(defaultTimezone) + '<br />' + timezoneHelp + '</td>\
-				<td>' + getCurrencySelector(defaultCurrency) + '<br />' + currencyHelp + '</td>\
-				<td>' + getEcommerceSelector(0) + '<br />' + ecommerceHelp + '</td>\
-				<td>' + submitButtonHtml + '</td>\
-	  			<td><span class="cancel link_but">' + sprintf(_pk_translate('General_OrCancel'), "", "") + '</span></td>\
-	 		</tr>'))
-                .appendTo('#editSites')
-            ;
-
-            piwikHelper.lazyScrollTo('#' + newRowId);
-
-            $('.addsite').click(function () {
-                sendAddSiteAJAX($('tr#' + newRowId));
-            });
-
-            $('.cancel').click(function () {
-                piwikHelper.hideAjaxError();
-                $(this).parents('tr').remove();
-                $('.addRowSite').toggle();
-            });
-            return false;
-        });
-
-        // when click on deleteuser, the we ask for confirmation and then delete the user
-        $('.deleteSite').click(function () {
-                piwikHelper.hideAjaxError();
-                var idRow = $(this).attr('id');
-                var nameToDelete = $(this).parent().parent().find('input#siteName').val() || $(this).parent().parent().find('td#siteName').html();
-                var idsiteToDelete = $(this).parent().parent().find('#idSite').html();
-
-                $('#confirm').find('h2').text(sprintf(_pk_translate('SitesManager_DeleteConfirm'), '"' + nameToDelete + '" (idSite = ' + idsiteToDelete + ')'));
-                piwikHelper.modalConfirm('#confirm', { yes: function () {
-
-                    sendDeleteSiteAJAX(idsiteToDelete);
-                }});
-            }
-        );
-
-        var alreadyEdited = [];
-        $('.editSite')
-            .click(function () {
-                piwikHelper.hideAjaxError();
-                var idRow = $(this).attr('id');
-                if (alreadyEdited[idRow] == 1) return;
-                if (siteBeingEdited) {
-                    $('#alert').find('h2').text(sprintf(_pk_translate('SitesManager_OnlyOneSiteAtTime'), '"' + $("<div/>").html(siteBeingEditedName).text() + '"'));
-                    piwikHelper.modalConfirm('#alert', {});
-                    return;
-                }
-                siteBeingEdited = true;
-
-                alreadyEdited[idRow] = 1;
-                $('tr#' + idRow + ' .editableSite').each(
-                    // make the fields editable
-                    // change the EDIT button to VALID button
-                    function (i, n) {
-                        var contentBefore = $(n).html();
-
-                        var idName = $(n).attr('id');
-                        if (idName == 'siteName') {
-                            siteBeingEditedName = contentBefore;
-                            var contentAfter = '<input id="' + idName + '" value="' + piwikHelper.htmlEntities( piwikHelper.htmlDecode(contentBefore))+ '" size="15" />';
-
-                            var inputSave = $('<br/><input style="margin-top:50px" type="submit" class="submit" value="' + _pk_translate('General_Save') + '" />')
-                                .click(function () { submitUpdateSite($(this).parent()); });
-                            var spanCancel = $('<div><br/>' + sprintf(_pk_translate('General_OrCancel'), "", "") + '</div>')
-                                .click(function () { piwikHelper.refreshAfter(0); });
-                            $(n)
-                                .html(contentAfter)
-                                .keypress(submitSiteOnEnter)
-                                .append(inputSave)
-                                .append(spanCancel);
-                        }
-                        else if (idName == 'urls') {
-                            var keepURLFragmentsForSite = $(this).closest('tr').attr('data-keep-url-fragments');
-                            var contentAfter = '<textarea cols="25" rows="3" id="urls">' + contentBefore.replace(/<br *\/? *> */gi, "\n") + '</textarea>';
-                            contentAfter += '<br />' + aliasUrlsHelp + keepURLFragmentSelectHTML;
-                            $(n).html(contentAfter).find('select').val(keepURLFragmentsForSite);
-                        }
-                        else if (idName == 'excludedIps') {
-                            var contentAfter = '<textarea cols="20" rows="4" id="excludedIps">' + contentBefore.replace(/<br *\/? *>/gi, "\n") + '</textarea>';
-                            contentAfter += '<br />' + excludedIpHelp;
-                            $(n).html(contentAfter);
-                        }
-                        else if (idName == 'excludedQueryParameters') {
-                            var contentAfter = '<textarea cols="20" rows="4" id="excludedQueryParameters">' + contentBefore.replace(/<br *\/? *>/gi, "\n") + '</textarea>';
-                            contentAfter += '<br />' + excludedQueryParametersHelp;
-                            $(n).html(contentAfter);
-                        }
-                        else if (idName == 'excludedUserAgents') {
-                            var contentAfter = '<textarea cols="20" rows="4" id="excludedUserAgents">' +
-                                contentBefore.replace(/<br *\/? *>/gi, "\n") + '</textarea><br />' + excludedUserAgentsHelp;
-                            $(n).html(contentAfter);
-                        }
-                        else if (idName == 'timezone') {
-                            var contentAfter = getTimezoneSelector(contentBefore);
-                            contentAfter += '<br />' + timezoneHelp;
-                            $(n).html(contentAfter);
-                        }
-                        else if (idName == 'currency') {
-                            var contentAfter = getCurrencySelector(contentBefore);
-                            contentAfter += '<br />' + currencyHelp;
-                            $(n).html(contentAfter);
-                        }
-                        else if (idName == 'ecommerce') {
-                            var ecommerceActive = contentBefore.indexOf("ecommerceActive") > 0 ? 1 : 0;
-                            contentAfter = getEcommerceSelector(ecommerceActive) + '<br />' + ecommerceHelp;
-                            $(n).html(contentAfter);
-                        }
-                        else if (idName == 'sitesearch') {
-                            contentAfter = getSitesearchSelector(contentBefore);
-                            $(n).html(contentAfter);
-                            onClickSiteSearchUseDefault();
-                        }
-                    }
-                );
-                $(this)
-                    .toggle()
-                    .parent()
-                    .prepend($('<input type="submit" class="updateSite submit" value="' + _pk_translate('General_Save') + '" />')
-                        .click(function () { sendUpdateSiteAJAX($('tr#' + idRow)); })
-                    );
-            });
-
-        $('#globalSettingsSubmit').click(function () {
-            sendGlobalSettingsAJAX();
-        });
-
-        $('#defaultTimezone').html(getTimezoneSelector(defaultTimezone));
-        $('#defaultCurrency').html(getCurrencySelector(defaultCurrency));
-
-        $('td.editableSite').click(function (event) {
-            $(this).parent().find('.editSite').click();
-        });
-    };
-
-    function getSitesearchSelector(contentBefore) {
-        var globalKeywordParameters = $('input#globalSearchKeywordParameters').val().trim();
-        var globalCategoryParameters = $('input#globalSearchCategoryParameters').val().trim();
-        if (contentBefore) {
-            var enabled = contentBefore.indexOf("sitesearchActive") > 0 ? 1 : 0;
-            var spanSearch = $(contentBefore).filter('.sskp');
-            var searchKeywordParameters = spanSearch.attr('sitesearch_keyword_parameters').trim();
-            var searchCategoryParameters = spanSearch.attr('sitesearch_category_parameters').trim();
-            var checked = globalKeywordParameters.length && !searchKeywordParameters.trim().length;
-        } else {
-            var searchKeywordParameters = globalKeywordParameters;
-            var searchCategoryParameters = globalCategoryParameters;
-            var enabled = searchKeywordParameters.length || searchCategoryParameters.length; // default is enabled
-            var checked = enabled;
-        }
-
-        var searchGlobalHasValues = globalKeywordParameters.trim().length;
-        var html = '<select id="sitesearch" onchange="return onClickSiteSearchUseDefault();">';
-        var selected = ' selected="selected" ';
-        html += '<option ' + (enabled ? selected : '') + ' value="1">' + sitesearchEnabled + '</option>';
-        html += '<option ' + (enabled ? '' : selected) + ' value="0">' + sitesearchDisabled + '</option>';
-        html += '</select>';
-        html += '<span style="font-size: 11px;"><br/>';
-
-        if (searchGlobalHasValues) {
-            var checkedStr = checked ? ' checked ' : '';
-            html += '<span id="sitesearchUseDefault"' + (!enabled ? ' style="display:none" ' : '') + '><input type="checkbox" '
-                + checkedStr + ' id="sitesearchUseDefaultCheck" onclick="return onClickSiteSearchUseDefault();"> '
-                + sitesearchUseDefault + ' </span>';
-                + '</label>';
-
-            html += '<div ' + ((checked && enabled) ? '' : 'style="display-none"') + ' class="searchDisplayParams form-description">'
-                + searchKeywordLabel + ' (' + strDefault + ') ' + ': '
-                + piwikHelper.htmlEntities( globalKeywordParameters )
-                + (globalCategoryParameters.length ? ', ' + searchCategoryLabel + ': ' + piwikHelper.htmlEntities(globalCategoryParameters) : '')
-                + '</div>';
-        }
-        html += '<div id="sitesearchIntro">' + sitesearchIntro + '</div>';
-
-        html += '<div id="searchSiteParameters">';
-        html += '<br/><label><div style="margin-bottom:3px">'
-                + piwikHelper.htmlEntities(searchKeywordLabel)
-                + '</div><input type="text" size="22" id="searchKeywordParameters" value="'
-                + piwikHelper.htmlEntities(searchKeywordParameters)
-                + '" style="font-size:9pt;font-family:monospace"></input>'
-                + searchKeywordHelp + '</label>';
-
-        // if custom var plugin is disabled, category tracking not supported
-        if (globalCategoryParameters != 'globalSearchCategoryParametersIsDisabled') {
-            html += '<br/><label><div style="margin-bottom:3px">' + searchCategoryLabel + '</div><input type="text" size="22" id="searchCategoryParameters" value="' + searchCategoryParameters + '" style="font-size:9pt;font-family:monospace"></input>' + searchCategoryHelp + '</label>';
-        }
-        html += '</div></span>';
-
-        return html;
-    }
-
-    function getEcommerceSelector(enabled) {
-        var html = '<select id="ecommerce">';
-        var selected = ' selected="selected" ';
-        html += '<option ' + (enabled ? '' : selected) + ' value="0">' + ecommerceDisabled + '</option>';
-        html += '<option ' + (enabled ? selected : '') + ' value="1">' + ecommerceEnabled + '</option>';
-        html += '</select>';
-        return html;
-    }
-
-    function getTimezoneSelector(selectedTimezone) {
-        var html = '<select id="timezones">';
-        for (var continent in timezones) {
-            html += '<optgroup label="' + continent + '">';
-            for (var timezoneId in timezones[continent]) {
-                var selected = '';
-                if (timezoneId == selectedTimezone) {
-                    selected = ' selected="selected" ';
-                }
-                html += '<option ' + selected + ' value="' + timezoneId + '">' + timezones[continent][timezoneId] + '</option>';
-            }
-            html += "</optgroup>\n";
-        }
-        html += '</select>';
-        return html;
-    }
-
-
-    function getCurrencySelector(selectedCurrency) {
-        var html = '<select id="currencies">';
-        for (var currency in currencies) {
-            var selected = '';
-            if (currency == selectedCurrency) {
-                selected = ' selected="selected" ';
-            }
-            html += '<option ' + selected + ' value="' + currency + '">' + currencies[currency] + '</option>';
-        }
-        html += '</select>';
-        return html;
-    }
-
-    function submitSiteOnEnter(e) {
-        var key = e.keyCode || e.which;
-        if (key == 13) {
-            submitUpdateSite(this);
-            $(this).find('.addsite').click();
-        }
-    }
-
-    function submitUpdateSite(self) {
-        $(self).parent().find('.updateSite').click();
-    }
-}
-
-function onClickSiteSearchUseDefault() {
-    // Site Search enabled
-    if ($('select#sitesearch').val() == "1") {
-        $('#sitesearchUseDefault').show();
-
-        // Use default is checked
-        if ($('#sitesearchUseDefaultCheck').is(':checked')) {
-            $('#searchSiteParameters').hide();
-            $('#sitesearchIntro').show();
-            $('#searchKeywordParameters,#searchCategoryParameters').val('');
-            $('.searchDisplayParams').show();
-            // Use default is unchecked
-
-        } else {
-            $('#sitesearchIntro').hide();
-            $('.searchDisplayParams').hide();
-            $('#searchSiteParameters').show();
-        }
-    } else {
-        $('.searchDisplayParams').hide();
-        $('#sitesearchUseDefault').hide();
-        $('#searchSiteParameters').hide();
-        $('#sitesearchIntro').show();
-    }
-}
-
-$(function () {
-
-    // when code element is clicked, select the text
-    $('.trackingHelp code').click(function() {
-        // credit where credit is due:
-        //   http://stackoverflow.com/questions/1173194/select-all-div-text-with-single-mouse-click
-        var range;
-        if (document.body.createTextRange) // MSIE
-        {
-            range = document.body.createTextRange();
-            range.moveToElementText(this);
-            range.select();
-        }
-        else if (window.getSelection) // others
-        {
-            range = document.createRange();
-            range.selectNodeContents(this);
-
-            var selection = window.getSelection();
-            selection.removeAllRanges();
-            selection.addRange(range);
-        }
-    })
-      .click();
-});
diff --git a/plugins/SitesManager/javascripts/sites-manager-controller.js b/plugins/SitesManager/javascripts/sites-manager-controller.js
new file mode 100644
index 0000000000000000000000000000000000000000..6da2f511e4988342663f4f8da4077e57be316f2b
--- /dev/null
+++ b/plugins/SitesManager/javascripts/sites-manager-controller.js
@@ -0,0 +1,244 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+angular.module('piwikApp').controller('SitesManagerController', function ($scope, $filter, coreAPI, coreAdminAPI, sitesManagerAPI, piwik, sitesManagerApiHelper) {
+
+    var translate = $filter('translate');
+
+    var init = function () {
+
+        initModel();
+        initActions();
+    };
+
+    var initModel = function() {
+
+        $scope.sites = [];
+        $scope.hasSuperUserAccess = piwik.hasSuperUserAccess;
+        $scope.redirectParams = {showaddsite: false};
+
+        initSelectLists();
+        initUtcTime();
+        initUserIP();
+        initCustomVariablesActivated();
+        initIsTimezoneSupportEnabled();
+        initGlobalParams();
+    };
+
+    var initActions = function () {
+
+        $scope.cancelEditSite = cancelEditSite;
+        $scope.addSite = addSite;
+        $scope.saveGlobalSettings = saveGlobalSettings;
+
+        $scope.informSiteIsBeingEdited = informSiteIsBeingEdited;
+        $scope.lookupCurrentEditSite = lookupCurrentEditSite;
+    };
+
+    var informSiteIsBeingEdited = function() {
+
+        $scope.siteIsBeingEdited = true;
+    };
+
+    var initSelectLists = function() {
+
+        initSiteSearchSelectOptions();
+        initEcommerceSelectOptions();
+        initCurrencyList();
+        initTimezones();
+    };
+
+    var initGlobalParams = function() {
+
+        showLoading();
+
+        sitesManagerAPI.getGlobalSettings(function(globalSettings) {
+
+            $scope.globalSettings = globalSettings;
+
+            $scope.globalSettings.searchKeywordParametersGlobal = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.globalSettings.searchKeywordParametersGlobal);
+            $scope.globalSettings.searchCategoryParametersGlobal = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.globalSettings.searchCategoryParametersGlobal);
+            $scope.globalSettings.excludedIpsGlobal = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.globalSettings.excludedIpsGlobal);
+            $scope.globalSettings.excludedQueryParametersGlobal = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.globalSettings.excludedQueryParametersGlobal);
+            $scope.globalSettings.excludedUserAgentsGlobal = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.globalSettings.excludedUserAgentsGlobal);
+
+            initKeepURLFragmentsList();
+
+            initSiteList();
+
+            triggerAddSiteIfRequested();
+        });
+    };
+
+    var triggerAddSiteIfRequested = function() {
+
+        if(piwikHelper.getArrayFromQueryString(String(window.location.search))['showaddsite'] == 1)
+            addSite();
+    };
+
+    var initEcommerceSelectOptions = function() {
+
+        $scope.eCommerceptions = [
+            {key: '0', value: translate('SitesManager_NotAnEcommerceSite')},
+            {key: '1', value: translate('SitesManager_EnableEcommerce')}
+        ];
+    };
+
+    var initUtcTime = function() {
+
+        var currentDate = new Date();
+
+        $scope.utcTime =  new Date(
+            currentDate.getUTCFullYear(),
+            currentDate.getUTCMonth(),
+            currentDate.getUTCDate(),
+            currentDate.getUTCHours(),
+            currentDate.getUTCMinutes(),
+            currentDate.getUTCSeconds()
+        );
+    };
+
+    var initIsTimezoneSupportEnabled = function() {
+
+        sitesManagerAPI.isTimezoneSupportEnabled(function (timezoneSupportEnabled) {
+            $scope.timezoneSupportEnabled = timezoneSupportEnabled;
+        });
+    };
+
+    var initTimezones = function() {
+
+        sitesManagerAPI.getTimezonesList(
+
+            function (timezones) {
+
+                $scope.timezones = [];
+
+                angular.forEach(timezones, function(groupTimezones, timezoneGroup) {
+
+                    angular.forEach(groupTimezones, function(label, code) {
+
+                        $scope.timezones.push({
+                           group: timezoneGroup,
+                           code: code,
+                           label: label
+                        });
+                    });
+                });
+            }
+        );
+    };
+
+    var initCustomVariablesActivated = function() {
+
+        coreAdminAPI.isPluginActivated(
+
+            function (customVariablesActivated) {
+                $scope.customVariablesActivated = customVariablesActivated;
+            },
+
+            {pluginName: 'CustomVariables'}
+        );
+    };
+
+    var initUserIP = function() {
+
+        coreAPI.getIpFromHeader(function(ip) {
+            $scope.currentIpAddress = ip;
+        });
+    };
+
+    var initSiteSearchSelectOptions = function() {
+
+        $scope.siteSearchOptions = [
+            {key: '1', value: translate('SitesManager_EnableSiteSearch')},
+            {key: '0', value: translate('SitesManager_DisableSiteSearch')}
+        ];
+    };
+
+    var initKeepURLFragmentsList = function() {
+
+        $scope.keepURLFragmentsOptions = {
+            0: ($scope.globalSettings.keepURLFragmentsGlobal ? translate('General_Yes') : translate('General_No')) + ' (' + translate('General_Default') + ')',
+            1: translate('General_Yes'),
+            2: translate('General_No')
+        };
+    };
+
+    var addSite = function() {
+        $scope.sites.push({});
+    };
+
+    var saveGlobalSettings = function() {
+
+        var ajaxHandler = new ajaxHelper();
+
+        ajaxHandler.addParams({
+            module: 'SitesManager',
+            format: 'json',
+            action: 'setGlobalSettings'
+        }, 'GET');
+
+        ajaxHandler.addParams({
+            timezone: $scope.globalSettings.defaultTimezone,
+            currency: $scope.globalSettings.defaultCurrency,
+            excludedIps: $scope.globalSettings.excludedIpsGlobal.join(','),
+            excludedQueryParameters: $scope.globalSettings.excludedQueryParametersGlobal.join(','),
+            excludedUserAgents: $scope.globalSettings.excludedUserAgentsGlobal.join(','),
+            keepURLFragments: $scope.globalSettings.keepURLFragmentsGlobal ? 1 : 0,
+            enableSiteUserAgentExclude: $scope.globalSettings.siteSpecificUserAgentExcludeEnabled ? 1 : 0,
+            searchKeywordParameters: $scope.globalSettings.searchKeywordParametersGlobal.join(','),
+            searchCategoryParameters: $scope.globalSettings.searchCategoryParametersGlobal.join(',')
+        }, 'POST');
+
+        ajaxHandler.redirectOnSuccess($scope.redirectParams);
+        ajaxHandler.setLoadingElement();
+        ajaxHandler.send(true);
+    };
+
+    var cancelEditSite = function ($event) {
+        $event.stopPropagation();
+        piwikHelper.redirect($scope.redirectParams);
+    };
+
+    var lookupCurrentEditSite = function () {
+
+        var sitesInEditMode = $scope.sites.filter(function(site) {
+            return site.editMode;
+        });
+
+        return sitesInEditMode[0];
+    };
+
+    var initSiteList = function () {
+
+        sitesManagerAPI.getSitesWithAdminAccess(function (sites) {
+
+            angular.forEach(sites, function(site) {
+                $scope.sites.push(site);
+            });
+
+            hideLoading();
+        });
+    };
+
+    var initCurrencyList = function () {
+
+        sitesManagerAPI.getCurrencyList(function (currencies) {
+            $scope.currencies = currencies;
+        });
+    };
+
+    var showLoading = function() {
+        $scope.loading = true;
+    };
+
+    var hideLoading = function() {
+        $scope.loading = false;
+    };
+
+    init();
+});
diff --git a/plugins/SitesManager/javascripts/sites-manager-directives.js b/plugins/SitesManager/javascripts/sites-manager-directives.js
new file mode 100644
index 0000000000000000000000000000000000000000..2357d3da607d231bcb011d37bf869b24fb5db2be
--- /dev/null
+++ b/plugins/SitesManager/javascripts/sites-manager-directives.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
+ */
+
+angular.module('piwikApp').directive('sitesManagerMultilineField', function () {
+
+    return {
+        restrict: 'A',
+        replace: true,
+        scope: {
+            managedValue: '=field',
+            rows: '@?',
+            cols: '@?'
+        },
+        templateUrl: 'plugins/SitesManager/templates/directives/multiline-field.html?cb=' + piwik.cacheBuster,
+        link: function (scope) {
+
+            var separator = '\n';
+
+            var init = function () {
+
+                scope.field = {};
+                scope.onChange = updateManagedScopeValue;
+
+                scope.$watch('managedValue', updateInputValue);
+            };
+
+            var updateManagedScopeValue = function () {
+                scope.managedValue = scope.field.value.trim().split(separator);
+            };
+
+            var updateInputValue = function () {
+
+                if(angular.isUndefined(scope.managedValue))
+                    return;
+
+                scope.field.value = scope.managedValue.join(separator);
+            };
+
+            init();
+        }
+    };
+});
+
+angular.module('piwikApp').directive('sitesManagerEditTrigger', function () {
+
+    return {
+        restrict: 'A',
+        link: function (scope, element) {
+
+            element.bind('click', function(){
+
+                if(!scope.site.editMode)
+                    scope.$apply(scope.editSite());
+            });
+
+            scope.$watch('site.editMode', function() {
+
+                element.toggleClass('editable-site-field', !scope.site.editMode);
+            });
+        }
+    };
+});
+
+angular.module('piwikApp').directive('sitesManagerScroll', function () {
+
+    return {
+        restrict: 'A',
+        link: function (scope, element) {
+
+            scope.$watch('site.editMode', function() {
+
+                if(scope.site.editMode)
+                    scrollToSite();
+            });
+
+            var scrollToSite = function() {
+                piwikHelper.lazyScrollTo(element[0], 500, true);
+            };
+        }
+    };
+});
diff --git a/plugins/SitesManager/javascripts/sites-manager-recipes.js b/plugins/SitesManager/javascripts/sites-manager-recipes.js
new file mode 100644
index 0000000000000000000000000000000000000000..a95fc8bef315a9b5d444bb85a1c30234b37db304
--- /dev/null
+++ b/plugins/SitesManager/javascripts/sites-manager-recipes.js
@@ -0,0 +1,95 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+angular.module('piwikApp').factory('sitesManagerAPI', function SitesManagerAPIFactory(sitesManagerApiHelper) {
+
+    var api = sitesManagerApiHelper;
+
+    return {
+        getCurrencyList: api.fetchApi('SitesManager.getCurrencyList', api.singleObjectAdaptor),
+        getSitesWithAdminAccess: api.fetchApi('SitesManager.getSitesWithAdminAccess', api.noop, {fetchAliasUrls: true}),
+        getTimezonesList: api.fetchApi('SitesManager.getTimezonesList', api.noop),
+        isTimezoneSupportEnabled: api.fetchApi('SitesManager.isTimezoneSupportEnabled', api.valueAdaptor),
+        getGlobalSettings: api.fetchAction('SitesManager', 'getGlobalSettings', api.singleObjectAdaptor)
+    };
+});
+
+// can probably be shared
+angular.module('piwikApp').factory('coreAPI', function CoreAPIFactory(sitesManagerApiHelper) {
+
+    var api = sitesManagerApiHelper;
+
+    return {
+        getIpFromHeader: api.fetchApi('API.getIpFromHeader', api.valueAdaptor)
+    };
+});
+
+// can probably be shared
+angular.module('piwikApp').factory('coreAdminAPI', function CoreAdminAPIFactory(sitesManagerApiHelper) {
+
+    var api = sitesManagerApiHelper;
+
+    return {
+        isPluginActivated: api.fetchApi('CoreAdminHome.isPluginActivated', api.valueAdaptor)
+    };
+});
+
+// can probably be renamed and shared
+angular.module('piwikApp').factory('sitesManagerApiHelper', function SitesManagerAPIHelperFactory(piwikApi) {
+
+    return {
+
+        fetch: function (endpoint, jsonResponseAdaptor, params) {
+
+            return function (clientHandover, additionalParams) {
+
+                params = angular.extend(params || {}, additionalParams || {});
+
+                var requestDefinition = angular.extend(endpoint, params);
+
+                var responseHandler = function (response) {
+
+                    response = jsonResponseAdaptor(response);
+
+                    clientHandover(response);
+                };
+
+                piwikApi.fetch(requestDefinition).then(responseHandler);
+            }
+        },
+
+        commaDelimitedFieldToArray: function(value) {
+
+            if(value == null || value == '')
+                return [];
+
+            return value.split(',');
+        },
+
+        fetchApi: function (apiMethod, jsonResponseAdaptor, params) {
+
+           return this.fetch({method: apiMethod}, jsonResponseAdaptor, params);
+        },
+
+        fetchAction: function (module, action, jsonResponseAdaptor, params) {
+
+            return this.fetch({module: module, action: action}, jsonResponseAdaptor, params);
+        },
+
+        singleObjectAdaptor: function (response) {
+            return response[0];
+        },
+
+        valueAdaptor: function (response) {
+            return response.value;
+        },
+
+        noop: function (response) {
+            return response;
+        }
+    };
+});
diff --git a/plugins/SitesManager/javascripts/sites-manager-site-controller.js b/plugins/SitesManager/javascripts/sites-manager-site-controller.js
new file mode 100644
index 0000000000000000000000000000000000000000..c6ea22e45d865254d99eb24c8e162988900bf183
--- /dev/null
+++ b/plugins/SitesManager/javascripts/sites-manager-site-controller.js
@@ -0,0 +1,164 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+angular.module('piwikApp').controller('SitesManagerSiteController', function ($scope, $filter, sitesManagerApiHelper) {
+
+    var translate = $filter('translate');
+
+    var init = function () {
+
+        initModel();
+        initActions();
+    };
+
+    var initActions = function () {
+
+        $scope.editSite = editSite;
+        $scope.saveSite = saveSite;
+        $scope.openDeleteDialog = openDeleteDialog;
+        $scope.site.delete = deleteSite;
+    };
+
+    var initModel = function() {
+
+        if(siteIsNew())
+            initNewSite();
+        else
+            initExistingSite();
+
+        $scope.site.editDialog = {};
+        $scope.site.removeDialog = {};
+    };
+
+    var editSite = function () {
+
+        if ($scope.siteIsBeingEdited) {
+
+            $scope.site.editDialog.show = true;
+            $scope.site.editDialog.title = translate('SitesManager_OnlyOneSiteAtTime', '"' + $scope.lookupCurrentEditSite().name + '"');
+
+        } else {
+
+            $scope.site.editMode = true;
+            $scope.informSiteIsBeingEdited();
+        }
+    };
+
+    var saveSite = function() {
+
+        var sendSiteSearchKeywordParams = $scope.site.sitesearch == '1' && !$scope.site.useDefaultSiteSearchParams;
+        var sendSearchCategoryParameters = sendSiteSearchKeywordParams && $scope.customVariablesActivated;
+
+        var ajaxHandler = new ajaxHelper();
+        ajaxHandler.addParams({
+            module: 'API',
+            format: 'json'
+        }, 'GET');
+
+        if(siteIsNew()) {
+
+            ajaxHandler.addParams({
+                method: 'SitesManager.addSite'
+            }, 'GET');
+
+        } else {
+
+            ajaxHandler.addParams({
+                idSite: $scope.site.idsite,
+                method: 'SitesManager.updateSite'
+            }, 'GET');
+        }
+
+        ajaxHandler.addParams({
+            siteName: $scope.site.name,
+            timezone: $scope.site.timezone,
+            currency: $scope.site.currency,
+            ecommerce: $scope.site.ecommerce,
+            excludedIps: $scope.site.excluded_ips.join(','),
+            excludedQueryParameters: $scope.site.excluded_parameters.join(','),
+            excludedUserAgents: $scope.site.excluded_user_agents.join(','),
+            keepURLFragments: $scope.site.keep_url_fragment,
+            siteSearch: $scope.site.sitesearch,
+            searchKeywordParameters: sendSiteSearchKeywordParams ? $scope.site.sitesearch_keyword_parameters.join(',') : null,
+            searchCategoryParameters: sendSearchCategoryParameters ? $scope.site.sitesearch_category_parameters.join(',') : null,
+            urls: $scope.site.alias_urls
+        }, 'POST');
+
+        ajaxHandler.redirectOnSuccess($scope.redirectParams);
+        ajaxHandler.setLoadingElement();
+        ajaxHandler.send(true);
+    };
+
+    var siteIsNew = function() {
+        return angular.isUndefined($scope.site.idsite);
+    };
+
+    var initNewSite = function() {
+
+        $scope.informSiteIsBeingEdited();
+
+        $scope.site.editMode = true;
+        $scope.site.name = "Name";
+        $scope.site.alias_urls = [
+            "http://siteUrl.com/",
+            "http://siteUrl2.com/"
+        ];
+        $scope.site.keep_url_fragment = "0";
+        $scope.site.excluded_ips = [];
+        $scope.site.excluded_parameters = [];
+        $scope.site.excluded_user_agents = [];
+        $scope.site.sitesearch_keyword_parameters = [];
+        $scope.site.sitesearch_category_parameters = [];
+        $scope.site.sitesearch = $scope.globalSettings.searchKeywordParametersGlobal.length ? "1" : "0";
+        $scope.site.timezone = $scope.globalSettings.defaultTimezone;
+        $scope.site.currency = $scope.globalSettings.defaultCurrency;
+        $scope.site.ecommerce = "0";
+
+        updateSiteWithSiteSearchConfig();
+    };
+
+    var initExistingSite = function() {
+
+        $scope.site.excluded_ips = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_ips);
+        $scope.site.excluded_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_parameters);
+        $scope.site.excluded_user_agents = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_user_agents);
+        $scope.site.sitesearch_keyword_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.sitesearch_keyword_parameters);
+        $scope.site.sitesearch_category_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.sitesearch_category_parameters);
+
+        updateSiteWithSiteSearchConfig();
+    };
+
+    var updateSiteWithSiteSearchConfig = function() {
+
+        $scope.site.useDefaultSiteSearchParams =
+            $scope.globalSettings.searchKeywordParametersGlobal.length && !$scope.site.sitesearch_keyword_parameters.length;
+    };
+
+    var openDeleteDialog = function() {
+
+        $scope.site.removeDialog.title = translate('SitesManager_DeleteConfirm', '"' + $scope.site.name + '" (idSite = ' + $scope.site.idsite + ')');
+        $scope.site.removeDialog.show = true;
+    };
+
+    var deleteSite = function() {
+
+        var ajaxHandler = new ajaxHelper();
+
+        ajaxHandler.addParams({
+            idSite: $scope.site.idsite,
+            module: 'API',
+            format: 'json',
+            method: 'SitesManager.deleteSite'
+        }, 'GET');
+
+        ajaxHandler.redirectOnSuccess($scope.redirectParams);
+        ajaxHandler.setLoadingElement();
+        ajaxHandler.send(true);
+    };
+
+    init();
+});
diff --git a/plugins/SitesManager/stylesheets/SitesManager.less b/plugins/SitesManager/stylesheets/SitesManager.less
index 3ff7fee9678158af8d2abb4cd66f8e41e462c691..de6c74f99018ba1f2baf289b55866bd2bd97d096 100644
--- a/plugins/SitesManager/stylesheets/SitesManager.less
+++ b/plugins/SitesManager/stylesheets/SitesManager.less
@@ -25,45 +25,14 @@
   vertical-align: middle;
 }
 
-#editSites {
-    vertical-align: top;
-}
-
-#editSites h4 {
-    font-size: .8em;
-    margin: 1em 0 1em 0;
-    font-weight: bold;
-}
-
-#editSites .entityTable tr td {
-    vertical-align: top;
-    padding-top: 7px;
-}
-
-#editSites .addRowSite:hover,
-#editSites .editableSite:hover,
-#editSites .addsite:hover,
-#editSites .cancel:hover,
-#editSites .deleteSite:hover,
-#editSites .editSite:hover,
-#editSites .updateSite:hover {
+td.editable-site-field:hover {
     cursor: pointer;
 }
 
-#editSites .addRowSite a {
-    text-decoration: none;
-}
-
-#editSites .addRowSite {
-    padding: 1em;
-    font-weight: bold;
-}
-
-.ecommerceInactive,
-.sitesearchInactive {
-    color: @theme-color-text-lighter;
+.link_but:hover {
+  cursor: pointer;
 }
 
-#searchSiteParameters {
-    display: none;
+.link_but:hover > span {
+  text-decoration: underline;
 }
\ No newline at end of file
diff --git a/plugins/SitesManager/templates/dialogs/dialogs.html b/plugins/SitesManager/templates/dialogs/dialogs.html
new file mode 100644
index 0000000000000000000000000000000000000000..476505e7bf44294bc873cb06f1ff45fe758400a8
--- /dev/null
+++ b/plugins/SitesManager/templates/dialogs/dialogs.html
@@ -0,0 +1,3 @@
+<div ng-include="'plugins/SitesManager/templates/dialogs/edit-dialog.html'"></div>
+
+<div ng-include="'plugins/SitesManager/templates/dialogs/remove-dialog.html'"></div>
diff --git a/plugins/SitesManager/templates/dialogs/edit-dialog.html b/plugins/SitesManager/templates/dialogs/edit-dialog.html
new file mode 100644
index 0000000000000000000000000000000000000000..afb9910869535d5348a8fc2182af6fe340caa4fc
--- /dev/null
+++ b/plugins/SitesManager/templates/dialogs/edit-dialog.html
@@ -0,0 +1,6 @@
+<div class="ui-confirm" piwik-dialog="site.editDialog.show">
+
+    <h2>{{ site.editDialog.title }}</h2>
+
+    <input role="no" type="button" value="{{ 'General_Ok'|translate }}"/>
+</div>
diff --git a/plugins/SitesManager/templates/dialogs/remove-dialog.html b/plugins/SitesManager/templates/dialogs/remove-dialog.html
new file mode 100644
index 0000000000000000000000000000000000000000..fee3d6ae89870aa8992468babc2c8c8c140e38a1
--- /dev/null
+++ b/plugins/SitesManager/templates/dialogs/remove-dialog.html
@@ -0,0 +1,7 @@
+<div class="ui-confirm" piwik-dialog="site.removeDialog.show" yes="site.delete()">
+
+    <h2>{{ site.removeDialog.title }}</h2>
+
+    <input type="button" value="{{ 'General_Yes'|translate }}" role="yes"/>
+    <input type="button" value="{{ 'General_No'|translate }}" role="no" />
+</div>
diff --git a/plugins/SitesManager/templates/directives/multiline-field.html b/plugins/SitesManager/templates/directives/multiline-field.html
new file mode 100644
index 0000000000000000000000000000000000000000..fed8822dc1d07d5714aad9b9b0474fbb7b52a5be
--- /dev/null
+++ b/plugins/SitesManager/templates/directives/multiline-field.html
@@ -0,0 +1,7 @@
+<textarea
+        cols="{{ cols }}"
+        rows="{{ rows }}"
+        ng-model="field.value"
+        ng-change="onChange()">
+
+</textarea>
diff --git a/plugins/SitesManager/templates/global-settings.html b/plugins/SitesManager/templates/global-settings.html
new file mode 100644
index 0000000000000000000000000000000000000000..049d7b5386a40b25c2b9846db2d3952b6a06db68
--- /dev/null
+++ b/plugins/SitesManager/templates/global-settings.html
@@ -0,0 +1,189 @@
+<div ng-show="hasSuperUserAccess">
+
+    <br/>
+
+    <h2 id="globalSettings">{{ 'SitesManager_GlobalWebsitesSettings' | translate }}</h2>
+
+    <br/>
+
+    <table style="width:600px;" class="adminTable">
+
+        <tr>
+            <td colspan="2">
+                <strong>{{ 'SitesManager_GlobalListExcludedIps'|translate }}</strong>
+
+                <p>{{ 'SitesManager_ListOfIpsToBeExcludedOnAllWebsites'|translate }} </p>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <div sites-manager-multiline-field field="globalSettings.excludedIpsGlobal" cols="30" rows="3" id="excludedIpsGlobal"></div>
+            </td>
+            <td>
+                <label for="excludedIpsGlobal" ng-include="'plugins/SitesManager/templates/help/excluded-ip-help.html'">
+                </label>
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2">
+                <strong>{{ 'SitesManager_GlobalListExcludedQueryParameters'|translate }}</strong>
+
+                <p>{{ 'SitesManager_ListOfQueryParametersToBeExcludedOnAllWebsites'|translate }} </p>
+            </td>
+        </tr>
+
+        <tr>
+            <td>
+                <div sites-manager-multiline-field field="globalSettings.excludedQueryParametersGlobal" cols="30" rows="3" id="excludedQueryParametersGlobal"></div>
+            </td>
+            <td>
+                <label for="excludedQueryParametersGlobal" ng-include="'plugins/SitesManager/templates/help/excluded-query-parameters-help.html'">
+                </label>
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2">
+                <strong>{{ 'SitesManager_GlobalListExcludedUserAgents'|translate }}</strong>
+
+                <p>{{ 'SitesManager_GlobalListExcludedUserAgents_Desc'|translate }}</p>
+            </td>
+        </tr>
+
+        <tr>
+            <td>
+                <div sites-manager-multiline-field field="globalSettings.excludedUserAgentsGlobal" cols="30" rows="3" id="excludedUserAgentsGlobal"></div>
+            </td>
+
+            <td>
+                <label for="excludedUserAgentsGlobal"  ng-include="'plugins/SitesManager/templates/help/excluded-user-agents-help.html'">
+                </label>
+            </td>
+        </tr>
+
+        <tr>
+            <td>
+                <input type="checkbox" id="siteSpecificUserAgentExcludeEnabled" ng-model="globalSettings.siteSpecificUserAgentExcludeEnabled">
+
+                <label for="siteSpecificUserAgentExcludeEnabled">
+                    {{ 'SitesManager_EnableSiteSpecificUserAgentExclude'|translate }}
+                </label>
+            </td>
+            <td>
+                <div class="ui-inline-help" ng-bind-html="'SitesManager_EnableSiteSpecificUserAgentExclude_Help'|translate:'<a href=\'#excludedUserAgentsGlobal\'>':'</a>'"></div>
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2">
+                <strong>{{ 'SitesManager_KeepURLFragments'|translate }}</strong>
+
+                <p ng-bind-html="'SitesManager_KeepURLFragmentsHelp'|translate:'<em>#</em>':'<em>example.org/index.html#first_section</em>':'<em>example.org/index.html</em>'"></p>
+
+                <input type="checkbox" id="keepURLFragmentsGlobal" ng-model="globalSettings.keepURLFragmentsGlobal">
+
+                <label for="keepURLFragmentsGlobal">{{ 'SitesManager_KeepURLFragmentsLong'|translate }}</label>
+
+                <p>{{ 'SitesManager_KeepURLFragmentsHelp2'|translate }}</p>
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2">
+
+                <strong>{{ 'SitesManager_TrackingSiteSearch'|translate }}</strong>
+
+                <p>{{ 'SitesManager_SiteSearchUse' | translate }}</p>
+
+                <span class="form-description" style="font-size:8pt;">
+                    {{ 'SitesManager_SearchParametersNote'|translate }} {{ 'SitesManager_SearchParametersNote2'|translate }}
+                </span>
+
+                <br/>
+                <br/>
+            </td>
+        </tr>
+
+        <tr>
+            <td>
+                <label>{{ 'SitesManager_SearchKeywordLabel' | translate }} &nbsp;
+                    <input ng-list size="15" ng-model="globalSettings.searchKeywordParametersGlobal">
+                </label>
+            </td>
+            <td>
+                <div class="ui-inline-help" style='width: 200px;'>
+                    {{ 'SitesManager_SearchKeywordParametersDesc' | translate }}
+                </div>
+
+                <br/>
+                <br/>
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2">
+                <span ng-hide="customVariablesActivated" class='form-description'>Note: you could also track your Internal Search Engine Categories, but the plugin Custom Variables is required. Please enable the plugin CustomVariables (or ask your Piwik admin).</span>
+                <span ng-show="customVariablesActivated"> {{ 'Goals_Optional'|translate }} {{ 'SitesManager_SearchCategoryDesc'|translate }} <br/> </span>
+
+                <br/>
+            </td>
+        </tr>
+
+        <tr ng-show="customVariablesActivated">
+            <td>
+                <label>{{ 'SitesManager_SearchCategoryLabel' | translate }}  &nbsp;
+                    <input ng-list size="15" ng-model="globalSettings.searchCategoryParametersGlobal">
+                </label>
+            </td>
+            <td>
+                <div class="ui-inline-help" style='width: 200px;'>
+                    {{ 'Goals_Optional'|translate }} {{ 'SitesManager_SearchCategoryParametersDesc'|translate }}
+                </div>
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2">
+                <strong>{{ 'SitesManager_DefaultTimezoneForNewWebsites'|translate }}</strong>
+
+                <p>{{ 'SitesManager_SelectDefaultTimezone'|translate }} </p>
+            </td>
+        </tr>
+
+        <tr>
+            <td>
+                <select
+                        ng-model="globalSettings.defaultTimezone"
+                        ng-options="t.code as t.label group by t.group for t in timezones">
+                </select>
+            </td>
+            <td ng-include="'plugins/SitesManager/templates/help/timezone-help.html'">
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2">
+                <strong>{{ 'SitesManager_DefaultCurrencyForNewWebsites'|translate }}</strong>
+
+                <p>{{ 'SitesManager_SelectDefaultCurrency'|translate }}</p>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <select
+                    ng-model="globalSettings.defaultCurrency"
+                    ng-options="k as v for (k, v) in currencies">
+                </select>
+            </td>
+            <td>
+                <div class="ui-inline-help">{{ 'SitesManager_CurrencySymbolWillBeUsedForGoals' | translate }}</div>
+            </td>
+        </tr>
+    </table>
+
+    <span style="margin-left:20px;">
+        <input type="submit" class="submit" ng-click="saveGlobalSettings()" value="{{ 'General_Save'|translate }}"/>
+    </span>
+
+</div>
diff --git a/plugins/SitesManager/templates/global-settings.twig b/plugins/SitesManager/templates/global-settings.twig
deleted file mode 100644
index 2d69ee15e3ef6d58f2877bf0d63f1d39d01d3f7d..0000000000000000000000000000000000000000
--- a/plugins/SitesManager/templates/global-settings.twig
+++ /dev/null
@@ -1,178 +0,0 @@
-{% if isSuperUser %}
-    <br/>
-
-    <h2 id="globalSettings">{{ 'SitesManager_GlobalWebsitesSettings'|translate }}</h2>
-    <br/>
-    <table style="width:600px;" class="adminTable">
-
-        <tr>
-            <td colspan="2">
-                <strong>{{ 'SitesManager_GlobalListExcludedIps'|translate }}</strong>
-
-                <p>{{ 'SitesManager_ListOfIpsToBeExcludedOnAllWebsites'|translate }} </p>
-            </td>
-        </tr>
-        <tr>
-            <td>
-                <textarea cols="30" rows="3" id="globalExcludedIps">
-                    {{- globalExcludedIps -}}
-                </textarea>
-            </td>
-            <td>
-                <label for="globalExcludedIps">{{ excludedIpHelp }}</label>
-            </td>
-        </tr>
-
-        <tr>
-            <td colspan="2">
-                <strong>{{ 'SitesManager_GlobalListExcludedQueryParameters'|translate }}</strong>
-
-                <p>{{ 'SitesManager_ListOfQueryParametersToBeExcludedOnAllWebsites'|translate }} </p>
-            </td>
-        </tr>
-
-        <tr>
-            <td>
-                <textarea cols="30" rows="3" id="globalExcludedQueryParameters">
-                    {{- globalExcludedQueryParameters -}}
-                </textarea>
-            </td>
-            <td>
-                <label for="globalExcludedQueryParameters">{{ excludedQueryParametersHelp }}</label>
-            </td>
-        </tr>
-
-        {# global excluded user agents #}
-        <tr>
-            <td colspan="2">
-                <strong>{{ 'SitesManager_GlobalListExcludedUserAgents'|translate }}</strong>
-
-                <p>{{ 'SitesManager_GlobalListExcludedUserAgents_Desc'|translate }}</p>
-            </td>
-        </tr>
-
-        <tr>
-            <td>
-                <textarea cols="30" rows="3" id="globalExcludedUserAgents">
-                    {{- globalExcludedUserAgents -}}
-                </textarea>
-            </td>
-            <td><label for="globalExcludedUserAgents">{{ excludedUserAgentsHelp }}</label>
-            </td>
-        </tr>
-
-        <tr>
-            <td>
-                <input type="checkbox" id="enableSiteUserAgentExclude" name="enableSiteUserAgentExclude"
-                       {% if allowSiteSpecificUserAgentExclude %}checked="checked"{% endif %}/>
-                <label for="enableSiteUserAgentExclude">
-                    {{ 'SitesManager_EnableSiteSpecificUserAgentExclude'|translate }}
-                </label>
-            <span id="enableSiteUserAgentExclude-loading" class="loadingPiwik" style="display:none;">
-                <img src="plugins/Morpheus/images/loading-blue.gif"/>
-            </span>
-            </td>
-            <td>
-                {{ piwik.inlineHelp('SitesManager_EnableSiteSpecificUserAgentExclude_Help'|translate('<a href="#editSites">','</a>'))|raw }}
-            </td>
-        </tr>
-
-        {# global keep URL fragments #}
-        <tr>
-            <td colspan="2">
-                <strong>{{ 'SitesManager_KeepURLFragments'|translate }}</strong>
-
-                <p>{{ 'SitesManager_KeepURLFragmentsHelp'|translate("<em>#</em>","<em>example.org/index.html#first_section</em>","<em>example.org/index.html</em>")|raw }}
-                </p>
-                <input type="checkbox" id="globalKeepURLFragments" name="globalKeepURLFragments"
-                       {% if globalKeepURLFragments %}checked="checked"{% endif %}/>
-                <label for="globalKeepURLFragments">{{ 'SitesManager_KeepURLFragmentsLong'|translate }}</label>
-
-                <p>{{ 'SitesManager_KeepURLFragmentsHelp2'|translate }}</p>
-            </td>
-        </tr>
-
-        {# global site search #}
-        <tr>
-            <td colspan="2">
-                <strong id="globalSiteSearch">{{ 'SitesManager_TrackingSiteSearch'|translate }}</strong>
-
-                <p>{{ sitesearchIntro }}</p>
-            <span class="form-description" style="font-size:8pt;">
-                {{ 'SitesManager_SearchParametersNote'|translate }} {{ 'SitesManager_SearchParametersNote2'|translate }}
-            </span>
-            </td>
-        </tr>
-        <tr>
-            <td colspan="2">
-                <label>{{ searchKeywordLabel }} &nbsp;
-                    <input type="text" size="15" id="globalSearchKeywordParameters"
-                           value="{{ globalSearchKeywordParameters|raw }}"/>
-
-                    <div style='width: 200px;float:right;'>{{ searchKeywordHelp }}</div>
-                </label>
-            </td>
-        </tr>
-
-        <tr>
-            <td colspan="2">
-                {% if not isSearchCategoryTrackingEnabled %}
-                <input value='globalSearchCategoryParametersIsDisabled' id="globalSearchCategoryParameters"
-                       type='hidden'/>
-                <span class='form-description'>Note: you could also track your Internal Search Engine Categories, but the plugin Custom Variables is required. Please enable the plugin CustomVariables (or ask your Piwik admin).</span>
-                {% else %}
-                {{ 'Goals_Optional'|translate }} {{ 'SitesManager_SearchCategoryDesc'|translate }} <br/>
-            </td>
-        </tr>
-        <tr>
-            <td colspan="2">
-                <label>{{ searchCategoryLabel }}  &nbsp;
-                    <input type="text" size="15" id="globalSearchCategoryParameters"
-                           value="{{ globalSearchCategoryParameters|raw }}"/>
-
-                    <div style='width: 200px;float:right;'>{{ searchCategoryHelp }}</div>
-                </label>
-                {% endif %}
-            </td>
-        </tr>
-
-        <tr>
-            <td colspan="2">
-                <strong>{{ 'SitesManager_DefaultTimezoneForNewWebsites'|translate }}</strong>
-
-                <p>{{ 'SitesManager_SelectDefaultTimezone'|translate }} </p>
-            </td>
-        </tr>
-        <tr>
-            <td>
-                <div id='defaultTimezone'></div>
-            </td>
-            <td>
-                {{ defaultTimezoneHelp }}
-            </td>
-        </tr>
-
-        <tr>
-            <td colspan="2">
-                <strong>{{ 'SitesManager_DefaultCurrencyForNewWebsites'|translate }}</strong>
-
-                <p>{{ 'SitesManager_SelectDefaultCurrency'|translate }}</p>
-            </td>
-        </tr>
-        <tr>
-            <td>
-                <div id='defaultCurrency'></div>
-            </td>
-            <td>
-                {{ currencyHelpPlain }}
-            </td>
-        </tr>
-    </table>
-
-    <span style="margin-left:20px;">
-        <input type="submit" class="submit" id='globalSettingsSubmit' value="{{ 'General_Save'|translate }}"/>
-    </span>
-
-    {{ ajax.errorDiv('ajaxErrorGlobalSettings') }}
-    {{ ajax.loadingDiv('ajaxLoadingGlobalSettings') }}
-{% endif %}
diff --git a/plugins/SitesManager/templates/help/excluded-ip-help.html b/plugins/SitesManager/templates/help/excluded-ip-help.html
new file mode 100644
index 0000000000000000000000000000000000000000..d652d1d4d1c7a4d16dcf0aa23538934441bbbb12
--- /dev/null
+++ b/plugins/SitesManager/templates/help/excluded-ip-help.html
@@ -0,0 +1,8 @@
+<div class="ui-inline-help">
+
+    {{ 'SitesManager_HelpExcludedIps' | translate : '1.2.3.*' : '1.2.*.*' }}
+
+    <br/><br/>
+
+    <span ng-bind-html="'SitesManager_YourCurrentIpAddressIs'|translate:'<i>' + currentIpAddress + '</i>'"></span>
+</div>
diff --git a/plugins/SitesManager/templates/help/excluded-query-parameters-help.html b/plugins/SitesManager/templates/help/excluded-query-parameters-help.html
new file mode 100644
index 0000000000000000000000000000000000000000..33b09e91c935e299520828ace5798a90344b7094
--- /dev/null
+++ b/plugins/SitesManager/templates/help/excluded-query-parameters-help.html
@@ -0,0 +1,8 @@
+<div class="ui-inline-help">
+
+    {{ 'SitesManager_ListOfQueryParametersToExclude'|translate }}
+
+    <br/><br/>
+
+    {{ 'SitesManager_PiwikWillAutomaticallyExcludeCommonSessionParameters'|translate:'phpsessid, sessionid, ...' }}
+</div>
diff --git a/plugins/SitesManager/templates/help/excluded-user-agents-help.html b/plugins/SitesManager/templates/help/excluded-user-agents-help.html
new file mode 100644
index 0000000000000000000000000000000000000000..d85fd6a79c9494b056998d4c5462d84bdf9c6f8a
--- /dev/null
+++ b/plugins/SitesManager/templates/help/excluded-user-agents-help.html
@@ -0,0 +1,8 @@
+<div class="ui-inline-help">
+
+    {{ 'SitesManager_GlobalExcludedUserAgentHelp1'|translate }}
+
+    <br/><br/>
+
+    {{ 'SitesManager_GlobalListExcludedUserAgents_Desc'|translate }} {{ 'SitesManager_GlobalExcludedUserAgentHelp2'|translate }}
+</div>
diff --git a/plugins/SitesManager/templates/help/timezone-help.html b/plugins/SitesManager/templates/help/timezone-help.html
new file mode 100644
index 0000000000000000000000000000000000000000..1787541516e71b4a3f8318ac789a33fb76352902
--- /dev/null
+++ b/plugins/SitesManager/templates/help/timezone-help.html
@@ -0,0 +1,21 @@
+<div class="ui-inline-help">
+
+    <span ng-switch="timezoneSupportEnabled">
+
+        <span ng-switch-default>
+            {{ 'SitesManager_AdvancedTimezoneSupportNotFound'|translate }}
+        </span>
+
+        <span ng-switch-when="true">
+            {{ 'SitesManager_ChooseCityInSameTimezoneAsYou'|translate }}
+        </span>
+    </span>
+
+    <br/><br/>
+
+    {{ 'SitesManager_UTCTimeIs'| translate : (utcTime | date : 'yyyy-MM-dd HH:mm:ss') }}
+
+    <br/><br/>
+
+    {{ 'SitesManager_ChangingYourTimezoneWillOnlyAffectDataForward'|translate }}
+</div>
diff --git a/plugins/SitesManager/templates/index.html b/plugins/SitesManager/templates/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..9ecd346dff45f1869eab5185e874c76fb03977e8
--- /dev/null
+++ b/plugins/SitesManager/templates/index.html
@@ -0,0 +1,11 @@
+<div ng-controller="SitesManagerController">
+
+    <div ng-include="'plugins/SitesManager/templates/sites-manager-header.html'"></div>
+
+    <div ng-include="'plugins/SitesManager/templates/loading.html'"></div>
+
+    <div ng-include="'plugins/SitesManager/templates/sites-list/sites-list.html'"></div>
+
+    <div ng-include="'plugins/SitesManager/templates/global-settings.html'"></div>
+
+</div>
diff --git a/plugins/SitesManager/templates/index.twig b/plugins/SitesManager/templates/index.twig
index 07204106407a2f5270c7dec9894bc9f22c5b0637..40cf84c0f2aea988726a79da3f96e5c284d6538e 100644
--- a/plugins/SitesManager/templates/index.twig
+++ b/plugins/SitesManager/templates/index.twig
@@ -2,65 +2,6 @@
 
 {% block content %}
 
-    {% import 'macros.twig' as piwik %}
-    {% import 'ajaxMacros.twig' as ajax %}
-
-    {% set excludedIpHelp %}
-        {{ 'SitesManager_HelpExcludedIps'|translate("1.2.3.*","1.2.*.*") }}
-        <br/><br/>
-        {{ 'SitesManager_YourCurrentIpAddressIs'|translate("<i>" ~ currentIpAddress ~ "</i>")|raw }}
-    {% endset %}
-    {% set excludedIpHelp=piwik.inlineHelp(excludedIpHelp) %}
-
-    {% set defaultTimezoneHelpPlain %}
-        {% if timezoneSupported %}
-            {{ 'SitesManager_ChooseCityInSameTimezoneAsYou'|translate }}
-        {% else %}
-            {{ 'SitesManager_AdvancedTimezoneSupportNotFound'|translate }}
-        {% endif %}
-        <br/><br/>
-        {{ 'SitesManager_UTCTimeIs'|translate(utcTime) }}
-    {% endset %}
-    {% set defaultTimezoneHelp=piwik.inlineHelp(defaultTimezoneHelpPlain) %}
-
-    {% set currencyHelpPlain %}
-        {{ piwik.inlineHelp('SitesManager_CurrencySymbolWillBeUsedForGoals'|translate) }}
-    {% endset %}
-
-    {% set excludedQueryParametersHelp %}
-        {{ 'SitesManager_ListOfQueryParametersToExclude'|translate }}
-        <br/><br/>
-        {{ 'SitesManager_PiwikWillAutomaticallyExcludeCommonSessionParameters'|translate("phpsessid, sessionid, ...") }}
-    {% endset %}
-    {% set excludedQueryParametersHelp=piwik.inlineHelp(excludedQueryParametersHelp) %}
-
-    {% set excludedUserAgentsHelp %}
-        {{ 'SitesManager_GlobalExcludedUserAgentHelp1'|translate }}
-        <br/><br/>
-        {{ 'SitesManager_GlobalListExcludedUserAgents_Desc'|translate }} {{ 'SitesManager_GlobalExcludedUserAgentHelp2'|translate }}
-    {% endset %}
-    {% set excludedUserAgentsHelp=piwik.inlineHelp(excludedUserAgentsHelp) %}
-
-    {% set searchCategoryHelpText %}
-        {{ 'Goals_Optional'|translate }} {{ 'SitesManager_SearchCategoryParametersDesc'|translate }}
-    {% endset %}
-
-    {% set searchCategoryHelp=piwik.inlineHelp(searchCategoryHelpText) %}
-
-    {% set searchCategoryLabel='SitesManager_SearchCategoryLabel'|translate %}
-
-    {% set searchKeywordHelp=piwik.inlineHelp('SitesManager_SearchKeywordParametersDesc'|translate) %}
-
-    {% set sitesearchIntro='SitesManager_SiteSearchUse'|translate %}
-
-    {% set searchKeywordLabel='SitesManager_SearchKeywordLabel'|translate %}
-
-    {% include '@SitesManager/sites-manager-heading.twig' %}
-
-    {% include '@SitesManager/sites-management.twig' %}
-
-    {% include '@SitesManager/global-settings.twig' %}
-
-    {% include '@SitesManager/init-script.twig' %}
+    <div ng-include="'plugins/SitesManager/templates/index.html'"></div>
 
 {% endblock %}
diff --git a/plugins/SitesManager/templates/init-script.twig b/plugins/SitesManager/templates/init-script.twig
deleted file mode 100644
index 98a1a5e1dd0c3ece70e5526eb90434dd7fd9313a..0000000000000000000000000000000000000000
--- a/plugins/SitesManager/templates/init-script.twig
+++ /dev/null
@@ -1,63 +0,0 @@
-{% set ecommerceHelpPlain %}
-    {{ 'SitesManager_EcommerceHelp'|translate }}
-    <br/>
-    {{ 'SitesManager_PiwikOffersEcommerceAnalytics'|translate("<a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'>","</a>")|raw }}
-{% endset %}
-
-{% set timezoneHelpPlain %}
-    {{ defaultTimezoneHelpPlain }}
-    <br/><br/>
-    {{ 'SitesManager_ChangingYourTimezoneWillOnlyAffectDataForward'|translate }}
-{% endset %}
-
-{% set keepURLFragmentSelectHTML %}
-    <h4 style="display:inline-block;">{{ 'SitesManager_KeepURLFragmentsLong'|translate }}</h4>
-
-    <select id="keepURLFragmentSelect">
-        <option value="0"> {% if globalKeepURLFragments %}{{ 'General_Yes'|translate }}{% else %}{{ 'General_No'|translate }}{% endif %}
-            ({{ 'General_Default'|translate }})
-        </option>
-        <option value="1">{{ 'General_Yes'|translate }}</option>
-        <option value="2">{{ 'General_No'|translate }}</option>
-    </select>
-{% endset %}
-
-<script type="text/javascript">
-
-    var excludedIpHelp = '{{ excludedIpHelp|e('js') }}';
-    var aliasUrlsHelp = '{{ piwik.inlineHelp('SitesManager_AliasUrlHelp'|translate)|e('js') }}';
-    var excludedQueryParametersHelp = '{{ excludedQueryParametersHelp|e('js') }}';
-    var excludedUserAgentsHelp = '{{ excludedUserAgentsHelp|e('js') }}';
-    var timezoneHelp = '{{ piwik.inlineHelp(timezoneHelpPlain)|e('js') }}';
-    var currencyHelp = '{{ currencyHelpPlain|e('js') }}';
-    var ecommerceHelp = '{{ piwik.inlineHelp(ecommerceHelpPlain)|e('js') }}';
-    var searchKeywordHelp = '{{ searchKeywordHelp|e('js') }}';
-    var searchCategoryHelp = '{{ searchCategoryHelp|e('js') }}';
-    var sitesearchDesc = '{{ 'SitesManager_TrackingSiteSearch'|translate|e('js') }}';
-
-    var ecommerceEnabled = '{{ 'SitesManager_EnableEcommerce'|translate|e('js') }}';
-    var ecommerceDisabled = '{{ 'SitesManager_NotAnEcommerceSite'|translate|e('js') }}';
-    var sitesearchEnabled = '{{ 'SitesManager_EnableSiteSearch'|translate|e('js') }}';
-    var sitesearchDisabled = '{{ 'SitesManager_DisableSiteSearch'|translate|e('js') }}';
-
-    var keepURLFragmentSelectHTML = '{{ keepURLFragmentSelectHTML|e('js') }}';
-
-    var searchKeywordLabel = '{{ searchKeywordLabel|e('js') }}';
-    var searchCategoryLabel = '{{ searchCategoryLabel|e('js') }}';
-
-    var sitesearchIntro = '{{ piwik.inlineHelp(sitesearchIntro)|e('js') }}';
-    var sitesearchUseDefault = '{% if isSuperUser %}{{ 'SitesManager_SearchUseDefault'|translate('<a href="#globalSiteSearch">','</a>')|e('js') }}{% else %}{{ 'SitesManager_SearchUseDefault'|translate('','')|e('js') }}{% endif %}';
-    var strDefault = '{{ 'General_Default'|translate|e('js') }}';
-
-    var sitesManager = new SitesManager({{ timezones|raw }}, {{ currencies|raw }}, '{{ defaultTimezone|raw }}', '{{ defaultCurrency|raw }}');
-
-    $(function () {
-        sitesManager.init();
-    });
-
-    {% if showAddSite %}
-        $(document).ready(function () {
-            $('.addRowSite:first').trigger('click');
-        });
-    {% endif %}
-</script>
\ No newline at end of file
diff --git a/plugins/SitesManager/templates/loading.html b/plugins/SitesManager/templates/loading.html
new file mode 100644
index 0000000000000000000000000000000000000000..6aaa13d4367942ca91947adc2b38f959b7e05817
--- /dev/null
+++ b/plugins/SitesManager/templates/loading.html
@@ -0,0 +1,6 @@
+<div ng-show="loading">
+    <div class="loadingPiwik">
+        <img src="plugins/Morpheus/images/loading-blue.gif" alt="{{ 'General_LoadingData'|translate }}" />
+        {{ 'General_LoadingData'|translate }}
+    </div>
+</div>
\ No newline at end of file
diff --git a/plugins/SitesManager/templates/sites-list/add-site-link.html b/plugins/SitesManager/templates/sites-list/add-site-link.html
new file mode 100644
index 0000000000000000000000000000000000000000..fd172552696250d30fbb4dae49179fb7bb2f2088
--- /dev/null
+++ b/plugins/SitesManager/templates/sites-list/add-site-link.html
@@ -0,0 +1,3 @@
+<a ng-show="hasSuperUserAccess && !siteIsBeingEdited" class="addRowSite" ng-click="addSite()">
+    {{ 'SitesManager_AddSite'|translate }}
+</a>
diff --git a/plugins/SitesManager/templates/sites-list/alias-urls-field.html b/plugins/SitesManager/templates/sites-list/alias-urls-field.html
new file mode 100644
index 0000000000000000000000000000000000000000..456f64c446aee0b8b0d24435837ab57c859f2908
--- /dev/null
+++ b/plugins/SitesManager/templates/sites-list/alias-urls-field.html
@@ -0,0 +1,23 @@
+<span ng-switch-default>
+    <div ng-repeat="url in site.alias_urls">
+        {{ url | prettyUrl }}
+    </div>
+</span>
+
+<span ng-switch-when="true">
+
+    <div sites-manager-multiline-field field="site.alias_urls" cols="25" rows="3"></div>
+
+    <div class="ui-inline-help">
+        {{ 'SitesManager_AliasUrlHelp' | translate }}
+    </div>
+
+    <h4 style="display:inline-block;">
+        {{ 'SitesManager_KeepURLFragmentsLong'|translate }}
+    </h4>
+
+    <select ng-options="key as value for (key, value) in keepURLFragmentsOptions"
+            ng-model="site.keep_url_fragment"
+            >
+    </select>
+</span>
diff --git a/plugins/SitesManager/templates/sites-list/site-fields.html b/plugins/SitesManager/templates/sites-list/site-fields.html
new file mode 100644
index 0000000000000000000000000000000000000000..7e8e2d4587866bc1cae08a8dbcc27c107bb6b42a
--- /dev/null
+++ b/plugins/SitesManager/templates/sites-list/site-fields.html
@@ -0,0 +1,182 @@
+<td sites-manager-edit-trigger>
+    {{ site.idsite }}
+</td>
+
+<td ng-switch="site.editMode" sites-manager-edit-trigger>
+
+    <span ng-switch-default>
+        {{ site.name }}
+    </span>
+
+    <span ng-switch-when="true">
+
+        <input type="text" ng-model="site.name"/>
+
+        <br/>
+        <br/>
+
+        <input type="submit" class="submit" value="{{ 'General_Save' | translate }}" ng-click="saveSite()"/>
+
+        <br/>
+        <br/>
+
+        <span class="link_but" ng-click="cancelEditSite($event)">
+            <span>{{ 'General_OrCancel' | translate:'':'' }}</span>
+        </span>
+    </span>
+
+</td>
+
+<td
+    sites-manager-edit-trigger
+    ng-include="'plugins/SitesManager/templates/sites-list/alias-urls-field.html'"
+    ng-switch="site.editMode">
+</td>
+
+<td ng-switch="site.editMode" sites-manager-edit-trigger>
+
+    <span ng-switch-default>
+        <div ng-repeat="ip in site.excluded_ips">
+            {{ ip }}
+        </div>
+    </span>
+
+    <span ng-switch-when="true">
+
+        <div sites-manager-multiline-field field="site.excluded_ips" cols="20" rows="4"></div>
+
+        <div ng-include="'plugins/SitesManager/templates/help/excluded-ip-help.html'"></div>
+    </span>
+
+</td>
+
+<td ng-switch="site.editMode" sites-manager-edit-trigger>
+
+    <span ng-switch-default>
+        <div ng-repeat="parameter in site.excluded_parameters">
+            {{ parameter }}
+        </div>
+    </span>
+
+    <span ng-switch-when="true">
+
+        <div sites-manager-multiline-field field="site.excluded_parameters" cols="20" rows="4"></div>
+
+        <div ng-include="'plugins/SitesManager/templates/help/excluded-query-parameters-help.html'"></div>
+    </span>
+
+</td>
+
+<td ng-show="globalSettings.siteSpecificUserAgentExcludeEnabled" ng-switch="site.editMode" sites-manager-edit-trigger>
+
+    <span ng-switch-default>
+        <div ng-repeat="userAgent in site.excluded_user_agents">
+            {{ userAgent }}
+        </div>
+    </span>
+
+    <span ng-switch-when="true">
+
+        <div sites-manager-multiline-field field="site.excluded_user_agents" cols="20" rows="4"></div>
+
+        <div ng-include="'plugins/SitesManager/templates/help/excluded-user-agents-help.html'"></div>
+    </span>
+
+</td>
+
+<td
+    sites-manager-edit-trigger
+    ng-include="'plugins/SitesManager/templates/sites-list/site-search-field.html'"
+    ng-switch="site.editMode">
+</td>
+
+
+<td ng-switch="site.editMode" sites-manager-edit-trigger>
+
+    <span ng-switch-default>
+        {{ site.timezone }}
+    </span>
+
+    <span ng-switch-when="true">
+        <select
+                ng-model="site.timezone"
+                ng-options="t.code as t.label group by t.group for t in timezones">
+        </select>
+
+        <div ng-include="'plugins/SitesManager/templates/help/timezone-help.html'"></div>
+
+    </span>
+</td>
+
+<td ng-switch="site.editMode" sites-manager-edit-trigger>
+
+    <span ng-switch-default>
+        {{ site.currency }}
+    </span>
+
+    <span ng-switch-when="true">
+
+        <select
+                ng-model="site.currency"
+                ng-options="k as v for (k, v) in currencies">
+        </select>
+
+        <div class="ui-inline-help">
+            {{ 'SitesManager_CurrencySymbolWillBeUsedForGoals' | translate }}
+        </div>
+
+    </span>
+</td>
+
+<td ng-switch="site.editMode" sites-manager-edit-trigger>
+
+    <span ng-switch-default ng-switch="site.ecommerce">
+
+        <span ng-switch-default>-</span>
+        <span ng-switch-when="1">{{ 'General_Yes'|translate }}</span>
+    </span>
+
+    <span ng-switch-when="true">
+
+        <select ng-options="option.key as option.value for option in eCommerceptions" ng-model="site.ecommerce">
+        </select>
+
+        <div class="ui-inline-help">
+            {{ 'SitesManager_EcommerceHelp' | translate }}
+            <br/>
+            <span ng-bind-html="'SitesManager_PiwikOffersEcommerceAnalytics'|translate:'<a href=\'http://piwik.org/docs/ecommerce-analytics/\' target=\'_blank\'>':'</a>'"></span>
+        </div>
+    </span>
+</td>
+
+<td ng-switch="site.editMode">
+
+    <span ng-switch-default class="link_but" ng-click="editSite()">
+        <img src='plugins/Morpheus/images/ico_edit.png'
+             title="{{ 'General_Edit'|translate }}"
+             border="0"
+                />
+        <span>{{ 'General_Edit'|translate }}</span>
+    </span>
+
+    <span ng-switch-when="true">
+
+        <input type="submit" class="submit" value="{{ 'General_Save' | translate }}" ng-click="saveSite()"/>
+    </span>
+</td>
+
+<td>
+    <span ng-show="site.idsite" class="link_but" ng-click="openDeleteDialog()">
+        <img
+                src='plugins/Morpheus/images/ico_delete.png'
+                title="{{ 'General_Delete'|translate }}"
+                border="0"/>
+        <span>{{ 'General_Delete'|translate }}</span>
+    </span>
+</td>
+
+<td>
+    <a ng-show="site.idsite" href="?module=CoreAdminHome&action=trackingCodeGenerator&idSite={{ site.idsite }}&updated=false">
+        {{ 'SitesManager_ShowTrackingTag'|translate }}
+    </a>
+</td>
diff --git a/plugins/SitesManager/templates/sites-list/site-search-field.html b/plugins/SitesManager/templates/sites-list/site-search-field.html
new file mode 100644
index 0000000000000000000000000000000000000000..c6754804e2a86154fd522cf05495992729cd819b
--- /dev/null
+++ b/plugins/SitesManager/templates/sites-list/site-search-field.html
@@ -0,0 +1,73 @@
+<span ng-switch-default ng-switch="site.sitesearch">
+    <span ng-switch-when="1">{{ 'General_Yes'|translate }}</span>
+    <span ng-switch-default>-</span>
+</span>
+
+<span ng-switch-when="true">
+
+    <select ng-options="option.key as option.value for option in siteSearchOptions" ng-model="site.sitesearch">
+    </select>
+
+    <div ng-show="site.sitesearch" style="font-size: 11px;">
+
+        <div ng-show="globalSettings.searchKeywordParametersGlobal.length">
+
+            <input type="checkbox" ng-model="site.useDefaultSiteSearchParams">
+
+            <span
+                    ng-show="hasSuperUserAccess"
+                    ng-bind-html="'SitesManager_SearchUseDefault'|translate:'<a href=\'#globalSettings\'>':'</a>'">
+            </span>
+            <span ng-hide="hasSuperUserAccess">
+                {{ 'SitesManager_SearchUseDefault' | translate:'':'' }}
+            </span>
+
+            <span ng-show="site.useDefaultSiteSearchParams" class="form-description">
+
+                {{ 'SitesManager_SearchKeywordLabel' | translate }}
+                ({{ 'General_Default' | translate }}) :
+
+                <span ng-repeat="param in globalSettings.searchKeywordParametersGlobal">
+                    {{ param }}<span ng-show="!$last">, </span>
+                </span>
+
+                <span ng-show="globalSettings.searchCategoryParametersGlobal.length">
+                    & {{ 'SitesManager_SearchCategoryLabel' | translate }} :
+                    <span ng-repeat="param in globalSettings.searchCategoryParametersGlobal">
+                        {{ param }}<span ng-show="!$last">, </span>
+                    </span>
+                </span>
+
+            </span>
+
+        </div>
+
+        <div ng-hide="site.useDefaultSiteSearchParams">
+
+            <br/>
+
+            <label>
+                {{ 'SitesManager_SearchKeywordLabel' | translate }}
+                <input ng-list ng-model="site.sitesearch_keyword_parameters">
+                <div class="ui-inline-help">
+                    {{ 'SitesManager_SearchKeywordParametersDesc' | translate }}
+                </div>
+            </label>
+
+            <label ng-show="customVariablesActivated">
+                {{ 'SitesManager_SearchCategoryLabel' | translate }}
+                <input ng-list ng-model="site.sitesearch_category_parameters">
+                <div class="ui-inline-help">
+                    {{ 'Goals_Optional' | translate }} {{ 'SitesManager_SearchCategoryParametersDesc' | translate }}
+                </div>
+            </label>
+
+        </div>
+
+    </div>
+
+    <div class="ui-inline-help" ng-show="site.sitesearch == 0 || site.useDefaultSiteSearchParams">
+        {{ 'SitesManager_SiteSearchUse' | translate }}
+    </div>
+
+</span>
diff --git a/plugins/SitesManager/templates/sites-list/sites-list.html b/plugins/SitesManager/templates/sites-list/sites-list.html
new file mode 100644
index 0000000000000000000000000000000000000000..8252585d223586d297929d636865b2e1721acf63
--- /dev/null
+++ b/plugins/SitesManager/templates/sites-list/sites-list.html
@@ -0,0 +1,39 @@
+<div class="entityContainer">
+
+    <div ng-repeat="site in sites" ng-include="'plugins/SitesManager/templates/dialogs/dialogs.html'"></div>
+
+    <div ng-include="'plugins/SitesManager/templates/sites-list/add-site-link.html'"></div>
+
+    <table class="entityTable dataTable">
+        <thead>
+        <tr>
+            <th>{{ 'General_Id'|translate }}</th>
+            <th>{{ 'General_Name'|translate }}</th>
+            <th>{{ 'SitesManager_Urls'|translate }}</th>
+            <th>{{ 'SitesManager_ExcludedIps'|translate }}</th>
+            <th>{{ 'SitesManager_ExcludedParameters'|translate }}</th>
+            <th ng-show="globalSettings.siteSpecificUserAgentExcludeEnabled">
+                {{ 'SitesManager_ExcludedUserAgents'|translate }}
+            </th>
+            <th>{{ 'Actions_SubmenuSitesearch'|translate }}</th>
+            <th>{{ 'SitesManager_Timezone'|translate }}</th>
+            <th>{{ 'SitesManager_Currency'|translate }}</th>
+            <th>{{ 'Goals_Ecommerce'|translate }}</th>
+            <th></th>
+            <th></th>
+            <th>{{ 'General_JsTrackingTag'|translate }}</th>
+        </tr>
+        </thead>
+        <tbody>
+            <tr
+                    sites-manager-scroll
+                    ng-controller="SitesManagerSiteController"
+                    ng-repeat="site in sites"
+                    ng-include="'plugins/SitesManager/templates/sites-list/site-fields.html'">
+            </tr>
+        </tbody>
+    </table>
+
+    <div ng-include="'plugins/SitesManager/templates/sites-list/add-site-link.html'"></div>
+
+</div>
diff --git a/plugins/SitesManager/templates/sites-management.twig b/plugins/SitesManager/templates/sites-management.twig
deleted file mode 100644
index 70fcf31d0afad2ce3bfbc7e35373510e737ef9f2..0000000000000000000000000000000000000000
--- a/plugins/SitesManager/templates/sites-management.twig
+++ /dev/null
@@ -1,125 +0,0 @@
-{{ ajax.errorDiv() }}
-{{ ajax.loadingDiv() }}
-
-{% set createNewWebsite %}
-<a href="javascript:" class="addRowSite">
-    {{ 'SitesManager_AddSite'|translate }}
-</a>
-{% endset %}
-
-{% if adminSites|length == 0 %}
-    {{ 'SitesManager_NoWebsites'|translate }}
-{% else %}
-
-    <div class="ui-confirm" id="confirm">
-        <h2></h2>
-        <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
-        <input role="no" type="button" value="{{ 'General_No'|translate }}"/>
-    </div>
-    <div class="entityContainer">
-        {% if isSuperUser %}
-            {{ createNewWebsite }}
-        {% endif %}
-        <table class="entityTable dataTable" id="editSites">
-            <thead>
-            <tr>
-                <th>{{ 'General_Id'|translate }}</th>
-                <th>{{ 'General_Name'|translate }}</th>
-                <th>{{ 'SitesManager_Urls'|translate }}</th>
-                <th>{{ 'SitesManager_ExcludedIps'|translate }}</th>
-                <th>{{ 'SitesManager_ExcludedParameters'|translate|replace({" ":"<br />"})|raw }}</th>
-                <th id='exclude-user-agent-header'
-                    {% if not allowSiteSpecificUserAgentExclude %}style="display:none;"{% endif %}>{{ 'SitesManager_ExcludedUserAgents'|translate }}</th>
-                <th>{{ 'Actions_SubmenuSitesearch'|translate }}</th>
-                <th>{{ 'SitesManager_Timezone'|translate }}</th>
-                <th>{{ 'SitesManager_Currency'|translate }}</th>
-                <th>{{ 'Goals_Ecommerce'|translate }}</th>
-                <th></th>
-                <th></th>
-                <th>{{ 'General_JsTrackingTag'|translate }}</th>
-            </tr>
-            </thead>
-            <tbody>
-            {% for i,site in adminSites %}
-                <tr id="row{{ site.idsite }}" data-keep-url-fragments="{{ site.keep_url_fragment }}">
-                    <td id="idSite">{{ site.idsite }}</td>
-                    <td id="siteName" class="editableSite">
-                        {{- site.name|raw -}}
-                    </td>
-                    <td id="urls" class="editableSite">
-                        {%- for url in site.alias_urls -%}
-                            {{- url|trim|replace({'http://': ''})|raw -}}<br />
-                        {%- endfor -%}
-                    </td>
-                    <td id="excludedIps" class="editableSite">
-                        {%- for ip in site.excluded_ips -%}
-                            {{- ip -}}<br/>
-                        {%- endfor -%}
-                    </td>
-                    <td id="excludedQueryParameters" class="editableSite">
-                        {%- for parameter in site.excluded_parameters -%}
-                            {{- parameter|raw -}}<br />
-                        {%- endfor -%}
-                    </td>
-                    <td id="excludedUserAgents" class="editableSite"
-                        {% if not allowSiteSpecificUserAgentExclude %}style="display:none;"{% endif %}>
-                        {%- for ua in site.excluded_user_agents -%}
-                            {{- ua|raw -}}<br />
-                        {%- endfor -%}
-                    </td>
-                    <td id="sitesearch" class="editableSite">
-                        {% if site.sitesearch %}
-                            <span class="sitesearchActive">{{ 'General_Yes'|translate }}</span>
-                        {% else %}
-                            <span class="sitesearchInactive">-</span>
-                        {% endif %}
-                        <span class="sskp" sitesearch_keyword_parameters="{{- site.sitesearch_keyword_parameters -}}"
-                              sitesearch_category_parameters="{{- site.sitesearch_category_parameters -}}"
-                              id="sitesearch_parameters">
-                        </span>
-                    </td>
-                    <td id="timezone" class="editableSite">{{ site.timezone }}</td>
-                    <td id="currency" class="editableSite">{{ site.currency }}</td>
-                    <td id="ecommerce" class="editableSite">
-                        {% if site.ecommerce %}
-                            <span class='ecommerceActive'>{{ 'General_Yes'|translate }}</span>
-                        {% else %}
-                            <span class='ecommerceInactive'>-</span>
-                        {% endif %}
-                    </td>
-                    <td>
-                <span id="row{{ site.idsite }}" class='editSite link_but'>
-                    <img src='plugins/Morpheus/images/ico_edit.png' title="{{ 'General_Edit'|translate }}"
-                         border="0"/>
-                    <span>{{ 'General_Edit'|translate }}</span>
-                </span>
-                    </td>
-                    <td>
-                <span id="row{{ site.idsite }}" class="deleteSite link_but">
-                    <img src='plugins/Morpheus/images/ico_delete.png' title="{{ 'General_Delete'|translate }}"
-                         border="0"/>
-                    <span>{{ 'General_Delete'|translate }}</span>
-                </span>
-                    </td>
-                    <td>
-                        <a href='{{ linkTo({'module':'CoreAdminHome','action':'trackingCodeGenerator','idSite':site.idsite,'updated':false }) }}'>
-                            {{ 'SitesManager_ShowTrackingTag'|translate }}
-                        </a>
-                    </td>
-                </tr>
-            {% endfor %}
-            </tbody>
-        </table>
-        {% if isSuperUser %}
-            {{ createNewWebsite }}
-        {% endif %}
-    </div>
-{% endif %}
-
-{# Admin users use these values for Site Search column, when editing websites #}
-{% if not isSuperUser %}
-    <input type="hidden" size="15" id="globalSearchKeywordParameters"
-           value="{{ globalSearchKeywordParameters }}"/>
-    <input type="hidden" size="15" id="globalSearchCategoryParameters"
-           value="{{ globalSearchCategoryParameters }}"/>
-{% endif %}
diff --git a/plugins/SitesManager/templates/sites-manager-header.html b/plugins/SitesManager/templates/sites-manager-header.html
new file mode 100644
index 0000000000000000000000000000000000000000..968947182faffe75d70aed676dc854c07c09b146
--- /dev/null
+++ b/plugins/SitesManager/templates/sites-manager-header.html
@@ -0,0 +1,18 @@
+<h2
+        piwik-enriched-headline
+        help-url="http://piwik.org/docs/manage-websites/"
+        feature-name="{{ 'SitesManager_WebsitesManagement'|translate }}">
+
+    {{ 'SitesManager_WebsitesManagement'|translate }}
+</h2>
+
+<p>
+    {{ 'SitesManager_MainDescription'|translate }}
+
+    <span ng-bind-html="'SitesManager_YouCurrentlyHaveAccessToNWebsites'|translate:'<strong>' + sites.length + '</strong>'"></span>
+
+    <span ng-show="hasSuperUserAccess">
+        <br/>
+        <span ng-bind-html="'SitesManager_SuperUserAccessCan'|translate:'<a href=\'#globalSettings\'>':'</a>'"></span>
+    </span>
+</p>
diff --git a/plugins/SitesManager/templates/sites-manager-heading.twig b/plugins/SitesManager/templates/sites-manager-heading.twig
deleted file mode 100644
index da72ec0c53dca7b00bfdb9e2c06adfe2dd36c85f..0000000000000000000000000000000000000000
--- a/plugins/SitesManager/templates/sites-manager-heading.twig
+++ /dev/null
@@ -1,12 +0,0 @@
-<h2 piwik-enriched-headline help-url="http://piwik.org/docs/manage-websites/">
-    {{ 'SitesManager_WebsitesManagement'|translate }}
-</h2>
-
-<p>
-    {{ 'SitesManager_MainDescription'|translate }}
-    {{ 'SitesManager_YouCurrentlyHaveAccessToNWebsites'|translate("<strong>" ~ adminSitesCount ~ "</strong>")|raw }}
-    {% if isSuperUser %}
-        <br/>
-        {{ 'SitesManager_SuperUserAccessCan'|translate("<a href='#globalSettings'>","</a>")|raw }}
-    {% endif %}
-</p>
\ No newline at end of file
diff --git a/tests/PHPUnit/Integration/expected/test_apiGetReportMetadata__API.getIpFromHeader.xml b/tests/PHPUnit/Integration/expected/test_apiGetReportMetadata__API.getIpFromHeader.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7c42ec92e02ae9bfa74a66adee0dd5cce5eccb27
--- /dev/null
+++ b/tests/PHPUnit/Integration/expected/test_apiGetReportMetadata__API.getIpFromHeader.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>127.0.0.1</result>
\ No newline at end of file