From 0a5cd9f113558a9c674af4924a9ff4ff366fd6f2 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Fri, 2 Sep 2016 16:26:47 +0200 Subject: [PATCH 1/7] Change dashboardcontroller to UmbracoApiController - Enables us to proxy css through the local server without a token on the css request - Adds method to load remote grid json - Adds method to load remote grid css --- .../Editors/DashboardController.cs | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 701cca0bb2..5b99153b45 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -6,14 +6,114 @@ 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 Umbraco.Core.Cache; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; +using System.Text; 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] + 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 + [WebApi.UmbracoAuthorize] + [ValidateAngularAntiForgeryToken] + public async Task GetRemoteDashboardContent(string section, string baseUrl = "http://dashboard.umbraco.org/") + { + var ctx = UmbracoContext.Current; + if (ctx == null) + throw new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); + + var user = Security.CurrentUser; + var userType = user.UserType.Alias; + var allowedSections = string.Join(",", user.AllowedSections); + var lang = 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, lang, version); + var key = "umb-dyn-dash-" + userType + lang + allowedSections + section; + + //look to see if we already have a requested result in the cache. + var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); + if (content != null) + return content; + try + { + var web = new HttpClient(); + + //fetch dashboard json and parse to JObject + var json = await web.GetStringAsync(url); + content = JObject.Parse(json); + + //save server content for 30 mins + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 30, 0)); + } + catch (Exception ex) + { + //set the content to an empty result and cache for 5 mins + //we return it like this, we avoid error codes which triggers UI warnings + + content = new JObject(); + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 5, 0)); + } + + return content; + } + + [WebApi.UmbracoAuthorize] + public async Task GetRemoteDashboardCss(string section, string baseUrl = "http://dashboard.umbraco.org/") + { + var cssUrl = string.Format(baseUrl + "css/dashboard.css?section={0}", section); + var key = "umb-dyn-dash-css-" + section; + + //look to see if we already have a requested result in the cache. + var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); + + //else fetch remotely + if (content == null) + { + try + { + var web = new HttpClient(); + + //fetch remote css + content = await web.GetStringAsync(cssUrl); + + //save server content for 30 mins + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 30, 0)); + } + catch (Exception ex) + { + //set the content to an empty result and cache for 5 mins + //we return it like this, we avoid error codes which triggers UI warnings + + content = string.Empty; + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 5, 0)); + } + } + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(content, Encoding.UTF8, "text/css") + }; + + } + + + [WebApi.UmbracoAuthorize] + [ValidateAngularAntiForgeryToken] public IEnumerable> GetDashboard(string section) { var tabs = new List>(); From 7c7dd0736e9b564afcfff1ec8081863c9a39f659 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Fri, 2 Sep 2016 16:27:30 +0200 Subject: [PATCH 2/7] DashboardBoard to render remote grid data - Includes offline mode - loading state - replaces content with data loaded from dashboard.umbraco.org --- .../dashboard/dashboard.tabs.controller.js | 33 ++++++ .../default/StartupDashboardIntro.html | 106 ++++++++++++------ 2 files changed, 106 insertions(+), 33 deletions(-) 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 53c0e0419d..ab840b4fa2 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..90d9b48969 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,84 @@ +
-
-

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:

- -

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 From f5d18157bfc8c53d5e6b7ebdd142bdef74fd21f4 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Fri, 2 Sep 2016 16:27:40 +0200 Subject: [PATCH 3/7] resource to load dashboard data --- .../common/resources/dashboard.resource.js | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) 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); } + + + }; } From 7611829f2e4f6cfc9294b038c5690fa6786f2c8a Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 29 Sep 2016 13:28:57 +0200 Subject: [PATCH 4/7] Changes http to https --- src/Umbraco.Web/Editors/DashboardController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 5b99153b45..7b186c0c42 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.Editors //we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side [WebApi.UmbracoAuthorize] [ValidateAngularAntiForgeryToken] - public async Task GetRemoteDashboardContent(string section, string baseUrl = "http://dashboard.umbraco.org/") + public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") { var ctx = UmbracoContext.Current; if (ctx == null) @@ -73,7 +73,7 @@ namespace Umbraco.Web.Editors } [WebApi.UmbracoAuthorize] - public async Task GetRemoteDashboardCss(string section, string baseUrl = "http://dashboard.umbraco.org/") + public async Task GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/") { var cssUrl = string.Format(baseUrl + "css/dashboard.css?section={0}", section); var key = "umb-dyn-dash-css-" + section; From b8575d04adb6e7218e645c0a506fef3ea204b600 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 18 Oct 2016 09:56:25 +0200 Subject: [PATCH 5/7] Adds default/fallback dashboard missing content --- .../dashboard/default/StartupDashboardIntro.html | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 90d9b48969..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 @@ -39,7 +39,18 @@

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:

+

+ 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:

+
From 33e05625edee59593ea814bef152a3036cafe5de Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 18 Oct 2016 10:10:50 +0200 Subject: [PATCH 6/7] Fixes based on review --- .../Editors/DashboardController.cs | 189 +++++++++--------- 1 file changed, 90 insertions(+), 99 deletions(-) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 7b186c0c42..fbfa28071c 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -4,7 +4,6 @@ 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; @@ -16,6 +15,7 @@ using Umbraco.Core.Cache; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using System.Text; +using Umbraco.Core.Logging; namespace Umbraco.Web.Editors { @@ -30,85 +30,79 @@ namespace Umbraco.Web.Editors //we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side [WebApi.UmbracoAuthorize] [ValidateAngularAntiForgeryToken] - public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") + public Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") { - var ctx = UmbracoContext.Current; - if (ctx == null) - throw new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); - + 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 lang = user.Language; + 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, lang, version); - var key = "umb-dyn-dash-" + userType + lang + allowedSections + section; + 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 timeSpan = new TimeSpan(0, 30, 0); - //look to see if we already have a requested result in the cache. - var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); - if (content != null) - return content; - try - { - var web = new HttpClient(); + return ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key, + async () => + { + try + { + using (var web = new HttpClient()) + { + //fetch dashboard json and parse to JObject + var json = await web.GetStringAsync(url); + return JObject.Parse(json); + } + } + catch (HttpRequestException ex) + { + LogHelper.Debug(string.Format("Error getting dashboard content from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException)); - //fetch dashboard json and parse to JObject - var json = await web.GetStringAsync(url); - content = JObject.Parse(json); - - //save server content for 30 mins - ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 30, 0)); - } - catch (Exception ex) - { - //set the content to an empty result and cache for 5 mins - //we return it like this, we avoid error codes which triggers UI warnings - - content = new JObject(); - ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 5, 0)); - } - - return content; + //set the content to an empty result and cache for 5 mins + //we return it like this, we avoid error codes which triggers UI warnings + timeSpan = new TimeSpan(0, 5, 0); + return new JObject(); + } + }, timeSpan); } [WebApi.UmbracoAuthorize] - public async Task GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/") + public Task GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/") { - var cssUrl = string.Format(baseUrl + "css/dashboard.css?section={0}", section); - var key = "umb-dyn-dash-css-" + section; + var url = string.Format(baseUrl + "css/dashboard.css?section={0}", section); + var key = "umbraco-dynamic-dashboard-css-" + section; + var timeSpan = new TimeSpan(0, 30, 0); - //look to see if we already have a requested result in the cache. - var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); - - //else fetch remotely - if (content == null) - { - try + return ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key, + async () => { - var web = new HttpClient(); + try + { + using (var web = new HttpClient()) + { + //fetch remote css + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(await web.GetStringAsync(url), Encoding.UTF8, "text/css") + }; + } + } + catch (HttpRequestException ex) + { + LogHelper.Debug(string.Format("Error getting dashboard CSS from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException)); - //fetch remote css - content = await web.GetStringAsync(cssUrl); - - //save server content for 30 mins - ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 30, 0)); - } - catch (Exception ex) - { - //set the content to an empty result and cache for 5 mins - //we return it like this, we avoid error codes which triggers UI warnings - - content = string.Empty; - ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => content, timeout: new TimeSpan(0, 5, 0)); - } - } - - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent(content, Encoding.UTF8, "text/css") - }; - + //we return it like this, we avoid error codes which triggers UI warnings + timeSpan = new TimeSpan(0, 5, 0); + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(string.Empty, Encoding.UTF8, "text/css") + }; + } + }, timeSpan); } @@ -123,50 +117,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; - } - } } From 9da95ce28fb49638e4a6803300a78593d54ade18 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 18 Oct 2016 14:16:37 +0200 Subject: [PATCH 7/7] Moved UmbracoAuthorize to the class level Unfortunately can't consolidate GetCacheItem and InsertCacheItem because of different timespan Can't directly cache the `content` variable due to problems with modified closures --- .../Editors/DashboardController.cs | 115 ++++++++++-------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index fbfa28071c..d668399e83 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -11,10 +11,10 @@ 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 System.Text; using Umbraco.Core.Logging; namespace Umbraco.Web.Editors @@ -25,12 +25,12 @@ namespace Umbraco.Web.Editors [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 - [WebApi.UmbracoAuthorize] [ValidateAngularAntiForgeryToken] - public Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") + public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") { var context = UmbracoContext.Current; if (context == null) @@ -44,69 +44,84 @@ namespace Umbraco.Web.Editors 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 timeSpan = new TimeSpan(0, 30, 0); - return ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key, - async () => + var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); + var result = new JObject(); + if (content != null) + { + result = content; + } + else + { + //content is null, go get it + try { - try + using (var web = new HttpClient()) { - using (var web = new HttpClient()) - { - //fetch dashboard json and parse to JObject - var json = await web.GetStringAsync(url); - return JObject.Parse(json); - } + //fetch dashboard json and parse to JObject + var json = await web.GetStringAsync(url); + content = JObject.Parse(json); + result = content; } - catch (HttpRequestException ex) - { - LogHelper.Debug(string.Format("Error getting dashboard content from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException)); - //set the content to an empty result and cache for 5 mins - //we return it like this, we avoid error codes which triggers UI warnings - timeSpan = new TimeSpan(0, 5, 0); - return new JObject(); - } - }, timeSpan); + 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; } - [WebApi.UmbracoAuthorize] - public Task GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/") + 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 timeSpan = new TimeSpan(0, 30, 0); - return ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key, - async () => + var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); + var result = string.Empty; + + if (content != null) + { + result = content; + } + else + { + //content is null, go get it + try { - try + using (var web = new HttpClient()) { - using (var web = new HttpClient()) - { - //fetch remote css - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent(await web.GetStringAsync(url), Encoding.UTF8, "text/css") - }; - } - } - catch (HttpRequestException ex) - { - LogHelper.Debug(string.Format("Error getting dashboard CSS from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException)); + //fetch remote css + content = await web.GetStringAsync(url); - //we return it like this, we avoid error codes which triggers UI warnings - timeSpan = new TimeSpan(0, 5, 0); - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent(string.Empty, Encoding.UTF8, "text/css") - }; + //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)); } - }, timeSpan); + } + 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") + }; } - - - [WebApi.UmbracoAuthorize] + [ValidateAngularAntiForgeryToken] public IEnumerable> GetDashboard(string section) {