diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/dashboard.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/dashboard.resource.js index ca3ae03876..c48b2dd2a7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/dashboard.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/dashboard.resource.js @@ -20,7 +20,6 @@ function dashboardResource($q, $http, umbRequestHelper) { * */ getDashboard: function (section) { - return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( @@ -28,7 +27,53 @@ function dashboardResource($q, $http, umbRequestHelper) { "GetDashboard", [{ section: section }])), 'Failed to get dashboard ' + section); + }, + + /** + * @ngdoc method + * @name umbraco.resources.dashboardResource#getRemoteDashboardContent + * @methodOf umbraco.resources.dashboardResource + * + * @description + * Retrieves dashboard content from a remote source for a given section + * + * @param {string} section Alias of section to retrieve dashboard content for + * @returns {Promise} resourcePromise object containing the user array. + * + */ + getRemoteDashboardContent: function (section, baseurl) { + + //build request values with optional params + var values = [{ section: section }]; + if (baseurl) + { + values.push({ baseurl: baseurl }); + } + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "dashboardApiBaseUrl", + "GetRemoteDashboardContent", + values)), "Failed to get dashboard content"); + }, + + getRemoteDashboardCssUrl: function (section, baseurl) { + + //build request values with optional params + var values = [{ section: section }]; + if (baseurl) { + values.push({ baseurl: baseurl }); + } + + return umbRequestHelper.getApiUrl( + "dashboardApiBaseUrl", + "GetRemoteDashboardCss", + values); } + + + }; } diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index 68d304198e..fb0c736f7b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -14,9 +14,42 @@ function startUpVideosDashboardController($scope, xmlhelper, $log, $http) { }); }; } + angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController); +function startUpDynamicContentController(dashboardResource, assetsService) { + var vm = this; + vm.loading = true; + vm.showDefault = false; + + //proxy remote css through the local server + assetsService.loadCss( dashboardResource.getRemoteDashboardCssUrl("content") ); + dashboardResource.getRemoteDashboardContent("content").then( + function (data) { + + vm.loading = false; + + //test if we have received valid data + //we capture it like this, so we avoid UI errors - which automatically triggers ui based on http response code + if (data && data.sections) { + vm.dashboard = data; + } else{ + vm.showDefault = true; + } + + }, + + function (exception) { + console.error(exception); + vm.loading = false; + vm.showDefault = true; + }); +} + +angular.module("umbraco").controller("Umbraco.Dashboard.StartUpDynamicContentController", startUpDynamicContentController); + + function FormsController($scope, $route, $cookieStore, packageResource, localizationService) { $scope.installForms = function(){ $scope.state = localizationService.localize("packager_installStateDownloading"); diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html index ff933a415e..73a6399b67 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html @@ -1,44 +1,95 @@ +
-
-

Welcome to The Friendly CMS

-

Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible:

+ -
-
- - Umbraco.TV - Hours of Umbraco Video Tutorials - +
+ +
+
+ +
+
- -

Umbraco.TV - Learn from the source!

-
+ +
+
-

- Umbraco.TV will help you go from zero to Umbraco - hero at a pace that suits you. Our easy to follow - online training videos will give you the fundamental - knowledge to start building awesome Umbraco websites. -

+ +
+
+
+
+
+ +
+
+ +
+
+
+ + +
+ -
- - Our Umbraco - + +
+

Welcome to The Friendly CMS

+

+ Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible. +

+

Find out more:

+ - -

Our Umbraco - The Friendliest Community

-
+
+
+ + Umbraco.TV - Hours of Umbraco Video Tutorials + -

- Our Umbraco - the official community site is your one - stop for everything Umbraco. Whether you need a - question answered or looking for cool plugins, the - worlds best community is just a click away. -

-
-
-
+ +

Umbraco.TV - Learn from the source!

+
+ +

+ Umbraco.TV will help you go from zero to Umbraco + hero at a pace that suits you. Our easy to follow + online training videos will give you the fundamental + knowledge to start building awesome Umbraco websites. +

+
+ +
+ + + Our Umbraco + + + +

Our Umbraco - The Friendliest Community

+
+ +

+ Our Umbraco - the official community site is your one + stop for everything Umbraco. Whether you need a + question answered or looking for cool plugins, the + worlds best community is just a click away. +

+ +
+
+
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 701cca0bb2..d668399e83 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -4,16 +4,125 @@ using Umbraco.Core.Configuration; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using System.Linq; -using System.Xml; using Umbraco.Core.IO; +using Newtonsoft.Json.Linq; +using System.Threading.Tasks; +using System.Net.Http; +using System.Web.Http; +using System; +using System.Net; +using System.Text; +using Umbraco.Core.Cache; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; +using Umbraco.Core.Logging; namespace Umbraco.Web.Editors { + //we need to fire up the controller like this to enable loading of remote css directly from this controller [PluginController("UmbracoApi")] - public class DashboardController : UmbracoAuthorizedJsonController + [ValidationFilter] + [AngularJsonOnlyConfiguration] + [IsBackOffice] + [WebApi.UmbracoAuthorize] + public class DashboardController : UmbracoApiController { + //we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side + [ValidateAngularAntiForgeryToken] + public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") + { + var context = UmbracoContext.Current; + if (context == null) + throw new HttpResponseException(HttpStatusCode.InternalServerError); + var user = Security.CurrentUser; + var userType = user.UserType.Alias; + var allowedSections = string.Join(",", user.AllowedSections); + var language = user.Language; + var version = UmbracoVersion.GetSemanticVersion().ToSemanticString(); + + var url = string.Format(baseUrl + "{0}?section={0}&type={1}&allowed={2}&lang={3}&version={4}", section, userType, allowedSections, language, version); + var key = "umbraco-dynamic-dashboard-" + userType + language + allowedSections.Replace(",", "-") + section; + + var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); + var result = new JObject(); + if (content != null) + { + result = content; + } + else + { + //content is null, go get it + try + { + using (var web = new HttpClient()) + { + //fetch dashboard json and parse to JObject + var json = await web.GetStringAsync(url); + content = JObject.Parse(json); + result = content; + } + + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); + } + catch (HttpRequestException ex) + { + LogHelper.Debug(string.Format("Error getting dashboard content from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException)); + + //it's still new JObject() - we return it like this to avoid error codes which triggers UI warnings + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 5, 0)); + } + } + + return result; + } + + public async Task GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/") + { + var url = string.Format(baseUrl + "css/dashboard.css?section={0}", section); + var key = "umbraco-dynamic-dashboard-css-" + section; + + var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); + var result = string.Empty; + + if (content != null) + { + result = content; + } + else + { + //content is null, go get it + try + { + using (var web = new HttpClient()) + { + //fetch remote css + content = await web.GetStringAsync(url); + + //can't use content directly, modified closure problem + result = content; + + //save server content for 30 mins + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); + } + } + catch (HttpRequestException ex) + { + LogHelper.Debug(string.Format("Error getting dashboard CSS from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException)); + + //it's still string.Empty - we return it like this to avoid error codes which triggers UI warnings + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 5, 0)); + } + } + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "text/css") + }; + } + + [ValidateAngularAntiForgeryToken] public IEnumerable> GetDashboard(string section) { var tabs = new List>(); @@ -23,50 +132,47 @@ namespace Umbraco.Web.Editors foreach( var dashboardSection in UmbracoConfig.For.DashboardSettings().Sections.Where(x => x.Areas.Contains(section))) { //we need to validate access to this section - if (DashboardSecurity.AuthorizeAccess(dashboardSection, Security.CurrentUser, Services.SectionService)) + if (DashboardSecurity.AuthorizeAccess(dashboardSection, Security.CurrentUser, Services.SectionService) == false) + continue; + + //User is authorized + foreach (var tab in dashboardSection.Tabs) { - //User is authorized - foreach (var dashTab in dashboardSection.Tabs) + //we need to validate access to this tab + if (DashboardSecurity.AuthorizeAccess(tab, Security.CurrentUser, Services.SectionService) == false) + continue; + + var dashboardControls = new List(); + + foreach (var control in tab.Controls) { - //we need to validate access to this tab - if (DashboardSecurity.AuthorizeAccess(dashTab, Security.CurrentUser, Services.SectionService)) - { - var props = new List(); + if (DashboardSecurity.AuthorizeAccess(control, Security.CurrentUser, Services.SectionService) == false) + continue; - foreach (var dashCtrl in dashTab.Controls) - { - if (DashboardSecurity.AuthorizeAccess(dashCtrl, Security.CurrentUser, - Services.SectionService)) - { - var ctrl = new DashboardControl(); - var controlPath = dashCtrl.ControlPath.Trim(' ', '\r', '\n'); - ctrl.Path = IOHelper.FindFile(controlPath); - if (controlPath.ToLower().EndsWith(".ascx")) - { - ctrl.ServerSide = true; - } - props.Add(ctrl); - } - } + var dashboardControl = new DashboardControl(); + var controlPath = control.ControlPath.Trim(); + dashboardControl.Path = IOHelper.FindFile(controlPath); + if (controlPath.ToLowerInvariant().EndsWith(".ascx".ToLowerInvariant())) + dashboardControl.ServerSide = true; - tabs.Add(new Tab - { - Id = i, - Alias = dashTab.Caption.ToSafeAlias(), - IsActive = i == 1, - Label = dashTab.Caption, - Properties = props - }); - i++; - } + dashboardControls.Add(dashboardControl); } + + tabs.Add(new Tab + { + Id = i, + Alias = tab.Caption.ToSafeAlias(), + IsActive = i == 1, + Label = tab.Caption, + Properties = dashboardControls + }); + + i++; } } //In case there are no tabs or a user doesn't have access the empty tabs list is returned return tabs; - } - } }