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 - 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.
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
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
-
+
+
+
+
+
-
- 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 - 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;
-
}
-
}
}