diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index 13dee96b97..60fba0ae40 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -1,4 +1,7 @@ -namespace Umbraco.Core +using System; +using System.ComponentModel; + +namespace Umbraco.Core { public static partial class Constants { @@ -15,6 +18,8 @@ /// /// The auth cookie name /// + [Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")] + [EditorBrowsable(EditorBrowsableState.Never)] public const string AuthCookieName = "UMB_UCONTEXT"; } diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index e332cb6dca..1c7c544ed8 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -15,7 +16,6 @@ using Microsoft.Owin; using Newtonsoft.Json; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Membership; -using Microsoft.Owin; using Umbraco.Core.Logging; namespace Umbraco.Core.Security @@ -157,9 +157,6 @@ namespace Umbraco.Core.Security return new HttpContextWrapper(http).GetCurrentIdentity(authenticateRequestIfNotFound); } - /// - /// This clears the forms authentication cookie - /// public static void UmbracoLogout(this HttpContextBase http) { if (http == null) throw new ArgumentNullException("http"); @@ -170,6 +167,8 @@ namespace Umbraco.Core.Security /// This clears the forms authentication cookie for webapi since cookies are handled differently /// /// + [Obsolete("Use OWIN IAuthenticationManager.SignOut instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public static void UmbracoLogoutWebApi(this HttpResponseMessage response) { if (response == null) throw new ArgumentNullException("response"); @@ -195,11 +194,8 @@ namespace Umbraco.Core.Security response.Headers.AddCookies(new[] { authCookie, prevCookie, extLoginCookie }); } - /// - /// This adds the forms authentication cookie for webapi since cookies are handled differently - /// - /// - /// + [Obsolete("Use WebSecurity.SetPrincipalForRequest")] + [EditorBrowsable(EditorBrowsableState.Never)] public static FormsAuthenticationTicket UmbracoLoginWebApi(this HttpResponseMessage response, IUser user) { if (response == null) throw new ArgumentNullException("response"); @@ -250,26 +246,29 @@ namespace Umbraco.Core.Security if (http == null) throw new ArgumentNullException("http"); new HttpContextWrapper(http).UmbracoLogout(); } - + /// - /// Renews the Umbraco authentication ticket + /// This will force ticket renewal in the OWIN pipeline /// /// /// public static bool RenewUmbracoAuthTicket(this HttpContextBase http) { if (http == null) throw new ArgumentNullException("http"); - return RenewAuthTicket(http, - UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, - UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain, - //Umbraco has always persisted it's original cookie for 1 day so we'll keep it that way - 1440); + http.Items["umbraco-force-auth"] = true; + return true; } + /// + /// This will force ticket renewal in the OWIN pipeline + /// + /// + /// internal static bool RenewUmbracoAuthTicket(this HttpContext http) { if (http == null) throw new ArgumentNullException("http"); - return new HttpContextWrapper(http).RenewUmbracoAuthTicket(); + http.Items["umbraco-force-auth"] = true; + return true; } /// @@ -390,8 +389,7 @@ namespace Umbraco.Core.Security //ensure there's def an expired cookie http.Response.Cookies.Add(new HttpCookie(c) { Expires = DateTime.Now.AddYears(-1) }); } - } - + } } private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName) @@ -432,51 +430,6 @@ namespace Umbraco.Core.Security return FormsAuthentication.Decrypt(formsCookie); } - /// - /// Renews the forms authentication ticket & cookie - /// - /// - /// - /// - /// - /// true if there was a ticket to renew otherwise false if there was no ticket - private static bool RenewAuthTicket(this HttpContextBase http, string cookieName, string cookieDomain, int minutesPersisted) - { - if (http == null) throw new ArgumentNullException("http"); - //get the ticket - var ticket = GetAuthTicket(http, cookieName); - //renew the ticket - var renewed = FormsAuthentication.RenewTicketIfOld(ticket); - if (renewed == null) - { - return false; - } - - //get the request cookie to get it's expiry date, - //NOTE: this will never be null becaues we already do this - // check in teh GetAuthTicket. - var formsCookie = http.Request.Cookies[cookieName]; - - //encrypt it - var hash = FormsAuthentication.Encrypt(renewed); - //write it to the response - var cookie = new HttpCookie(cookieName, hash) - { - Expires = DateTime.Now.AddMinutes(minutesPersisted), - Domain = cookieDomain - }; - - if (GlobalSettings.UseSSL) - cookie.Secure = true; - - //ensure http only, this should only be able to be accessed via the server - cookie.HttpOnly = true; - - //rewrite the cooke - http.Response.Cookies.Set(cookie); - return true; - } - /// /// Creates a custom FormsAuthentication ticket with the data specified /// diff --git a/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs b/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs index 5247ea0af4..086d1a77bf 100644 --- a/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs +++ b/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs @@ -1,12 +1,38 @@ +using System; using System.Globalization; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; +using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; +using Umbraco.Core.Configuration; namespace Umbraco.Core.Security { public class BackOfficeCookieAuthenticationProvider : CookieAuthenticationProvider { + public override void ResponseSignOut(CookieResponseSignOutContext context) + { + base.ResponseSignOut(context); + + //Make sure the definitely all of these cookies are cleared when signing out with cookies + context.Response.Cookies.Append(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "", new CookieOptions + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }); + context.Response.Cookies.Append(Constants.Web.PreviewCookieName, "", new CookieOptions + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }); + context.Response.Cookies.Append(Constants.Security.BackOfficeExternalCookieName, "", new CookieOptions + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }); + } + /// /// Ensures that the culture is set correctly for the current back office user /// diff --git a/src/Umbraco.Core/Security/BackOfficeSignInManager.cs b/src/Umbraco.Core/Security/BackOfficeSignInManager.cs index 85d6a0c715..f1d18b9d0f 100644 --- a/src/Umbraco.Core/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Core/Security/BackOfficeSignInManager.cs @@ -53,6 +53,11 @@ namespace Umbraco.Core.Security switch (result) { case SignInStatus.Success: + _logger.WriteCore(TraceEventType.Information, 0, + string.Format( + "User: {0} logged in from IP address {1}", + userName, + _request.RemoteIpAddress), null, null); break; case SignInStatus.LockedOut: _logger.WriteCore(TraceEventType.Information, 0, diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index 772009e89a..712f98aeb2 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -100,6 +100,8 @@ namespace Umbraco.Core.Services xml.Add(new XAttribute("loginName", member.Username)); xml.Add(new XAttribute("email", member.Email)); + + xml.Add(new XAttribute("icon", member.ContentType.Icon)); return xml; } diff --git a/src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs new file mode 100644 index 0000000000..3f327d3155 --- /dev/null +++ b/src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -0,0 +1,173 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; +using Umbraco.Core.Profiling; +using Umbraco.Web; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; + +namespace Umbraco.Tests.Mvc +{ + [TestFixture] + public class RenderIndexActionSelectorAttributeTests + { + private MethodInfo GetRenderMvcControllerIndexMethodFromCurrentType(Type currType) + { + return currType.GetMethods().Single(x => + { + if (x.Name != "Index") return false; + if (x.ReturnParameter == null || x.ReturnParameter.ParameterType != typeof (ActionResult)) return false; + var p = x.GetParameters(); + if (p.Length != 1) return false; + if (p[0].ParameterType != typeof (RenderModel)) return false; + return true; + }); + } + + [Test] + public void Matches_Default_Index() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesDefaultIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsTrue(result); + } + + [Test] + public void Matches_Overriden_Index() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesOverriddenIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsTrue(result); + } + + [Test] + public void Matches_Custom_Index() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesCustomIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsFalse(result); + } + + [Test] + public void Matches_Async_Index_Same_Signature() + { + var attr = new RenderIndexActionSelectorAttribute(); + var req = new RequestContext(); + var appCtx = new ApplicationContext( + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + var ctrl = new MatchesAsyncIndexController(umbCtx); + var controllerCtx = new ControllerContext(req, ctrl); + var result = attr.IsValidForRequest(controllerCtx, + GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType())); + + Assert.IsFalse(result); + } + + public class MatchesDefaultIndexController : RenderMvcController + { + public MatchesDefaultIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + } + + public class MatchesOverriddenIndexController : RenderMvcController + { + public MatchesOverriddenIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + + public override ActionResult Index(RenderModel model) + { + return base.Index(model); + } + } + + public class MatchesCustomIndexController : RenderMvcController + { + public MatchesCustomIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + + public ActionResult Index(RenderModel model, int page) + { + return base.Index(model); + } + } + + public class MatchesAsyncIndexController : RenderMvcController + { + public MatchesAsyncIndexController(UmbracoContext umbracoContext) : base(umbracoContext) + { + } + + public new async Task Index(RenderModel model) + { + return await Task.FromResult(base.Index(model)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs index c13818a03c..4c06d30796 100644 --- a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs +++ b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Text; using System.Web.Mvc; using NUnit.Framework; diff --git a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs index 31bdb616b0..7f38bf61a1 100644 --- a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs @@ -8,6 +8,7 @@ using System.Web; using System.Web.Routing; using Moq; using Umbraco.Core; +using Umbraco.Core.Configuration; namespace Umbraco.Tests.TestHelpers { @@ -60,7 +61,7 @@ namespace Umbraco.Tests.TestHelpers //Cookie collection var cookieCollection = new HttpCookieCollection(); - cookieCollection.Add(new HttpCookie(Constants.Web.AuthCookieName, "FBA996E7-D6BE-489B-B199-2B0F3D2DD826")); + cookieCollection.Add(new HttpCookie("UMB_UCONTEXT", "FBA996E7-D6BE-489B-B199-2B0F3D2DD826")); //Request var requestMock = new Mock(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 0e7dc711b8..59a9166e5a 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -180,6 +180,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/bower.json b/src/Umbraco.Web.UI.Client/bower.json index 9a6b26aeaf..6d5a5949cd 100644 --- a/src/Umbraco.Web.UI.Client/bower.json +++ b/src/Umbraco.Web.UI.Client/bower.json @@ -25,6 +25,7 @@ "jquery-ui": "1.10.3", "angular-dynamic-locale": "~0.1.27", "tinymce": "~4.1.10", - "codemirror": "~5.3.0" + "codemirror": "~5.3.0", + "ng-caps-lock": "~1.0.2" } } diff --git a/src/Umbraco.Web.UI.Client/gruntFile.js b/src/Umbraco.Web.UI.Client/gruntFile.js index 8699f4a6f1..b184dee673 100644 --- a/src/Umbraco.Web.UI.Client/gruntFile.js +++ b/src/Umbraco.Web.UI.Client/gruntFile.js @@ -459,7 +459,10 @@ module.exports = function (grunt) { }, 'angular-dynamic-locale': { files: ['tmhDynamicLocale.min.js', 'tmhDynamicLocale.min.js.map'] - }, + }, + 'ng-caps-lock': { + files: ['ng-caps-lock.min.js'] + }, 'codemirror': { files: [ 'lib/codemirror.js', diff --git a/src/Umbraco.Web.UI.Client/src/app.dev.js b/src/Umbraco.Web.UI.Client/src/app.dev.js index e4ce285755..e9ad8777d7 100644 --- a/src/Umbraco.Web.UI.Client/src/app.dev.js +++ b/src/Umbraco.Web.UI.Client/src/app.dev.js @@ -7,6 +7,7 @@ var app = angular.module('umbraco', [ 'ngCookies', 'ngMobile', 'ngSanitize', + 'ngCapsLock', /*'ui.sortable',*/ 'blueimp.fileupload', 'tmh.dynamicLocale' diff --git a/src/Umbraco.Web.UI.Client/src/app.js b/src/Umbraco.Web.UI.Client/src/app.js index c541e0776c..70968936c5 100644 --- a/src/Umbraco.Web.UI.Client/src/app.js +++ b/src/Umbraco.Web.UI.Client/src/app.js @@ -7,6 +7,7 @@ var app = angular.module('umbraco', [ 'ngCookies', 'ngSanitize', 'ngMobile', + 'ngCapsLock', 'blueimp.fileupload', 'tmh.dynamicLocale' ]); diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 74010962f3..b0e6c8f5c3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -133,7 +133,7 @@ h5{ .umb-control-group label.control-label { text-align: left } -.umb-control-group label .help-block, +.umb-control-group label .help-block, .umb-control-group label small { font-size: 11px; color: #a0a0a0; @@ -194,7 +194,7 @@ h5{ .umb-dashboard-control a{text-decoration: underline;} -.umb-dashboard-control +.umb-dashboard-control iframe{ position: absolute; display: block; width: 99%; height: 99%; overflow: auto !important;} @@ -239,16 +239,16 @@ table thead a { .umb-table tbody.ui-sortable tr.ui-sortable-helper { background-color: @sortableHelperBg; - border: none; + border: none; } -.umb-table tbody.ui-sortable tr.ui-sortable-helper td +.umb-table tbody.ui-sortable tr.ui-sortable-helper td { border:none; } .umb-table tbody.ui-sortable tr.ui-sortable-placeholder { background-color: @sortablePlaceholderBg; - border:none; + border:none; } .umb-table tbody.ui-sortable tr.ui-sortable-placeholder td @@ -368,7 +368,7 @@ table thead a { background: @inputBorder; height: 1px; margin: 20px 0 0 0; - width: 82%; + width: 82%; float: left; } @@ -437,7 +437,7 @@ table thead a { color:@green; } -// Loading Animation +// Loading Animation // ------------------------ .umb-loader{ @@ -523,11 +523,67 @@ height:1px; } .umb-loader-wrapper { - + position: absolute; right: 0; left: 0; margin: 10px 0; - overflow: hidden; + overflow: hidden; } + + +// Helpers + +.strong { + font-weight: bold; +} + +.inline { + display: inline; +} + + +// Input label styles +// @Simon: not sure where to put this part yet +// --- TODO Needs to be divided into the right .less directories + + +// Titles for input fields +.input-label--title { + font-weight: bold; + color: @black; + + margin-bottom: 3px; +} + + +// Used for input checkmark fields +.input-label--small { + display: inline; + + font-size: 12px; + font-weight: bold; + color: fade(@black, 70); + + &:hover { + color: @black; + } +} + +input[type=checkbox]:checked + .input-label--small { + color: @blue; +} + + +// Use this for headers in the panels +.panel-dialog--header { + border-bottom: 1px solid @gray; + + margin: 10px 0; + padding-bottom: 10px; + + font-size: @fontSizeLarge; + font-weight: bold; + line-height: 20px; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index 4e0cde9298..9fedbfc9eb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -42,6 +42,23 @@ bottom: 90px; } +.umb-mediapicker-upload { + display: -ms-flexbox; + display: -webkit-box; + display: -webkit-flex; + display: flex; + + .form-search { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + } + + .upload-button { + margin-left: 16px; + } +} + .umb-panel.editor-breadcrumb .umb-panel-body, .umb-panel.editor-breadcrumb .umb-bottom-bar { bottom: 31px !important; } diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index be6b1d7b06..19830800a5 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -40,8 +40,16 @@ padding: 10px; } -.umb-contentpicker small a { +.umb-contentpicker small { + + &:not(:last-child) { + padding-right: 3px; + border-right: 1px solid @grayMed; + } + + a { color: @gray; + } } /* CODEMIRROR DATATYPE */ diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index ec73e62c0c..f002566eed 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -14,6 +14,7 @@ @grayDarker: #222; @grayDark: #343434; @gray: #555; +@grayMed: #999; @grayLight: #d9d9d9; @grayLighter: #f8f8f8; @white: #fff; diff --git a/src/Umbraco.Web.UI.Client/src/loader.dev.js b/src/Umbraco.Web.UI.Client/src/loader.dev.js index 2be6593238..ff0fe23ecc 100644 --- a/src/Umbraco.Web.UI.Client/src/loader.dev.js +++ b/src/Umbraco.Web.UI.Client/src/loader.dev.js @@ -10,6 +10,8 @@ LazyLoad.js( 'lib/angular/1.1.5/angular-mobile.js', 'lib/angular/1.1.5/angular-sanitize.min.js', + 'lib/ng-caps-lock/ng-caps-lock.min.js', + 'lib/angular/angular-ui-sortable.js', 'lib/angular-dynamic-locale/tmhDynamicLocale.min.js', diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html index 94826b07b4..d58f8a2fd3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html @@ -41,6 +41,7 @@
+

Caps Lock is on

diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html index 480edc4181..b6bd76499f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html @@ -62,18 +62,16 @@
-
-
- +
+ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/snippet.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/snippet.controller.js new file mode 100644 index 0000000000..17e593ff86 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/snippet.controller.js @@ -0,0 +1,8 @@ +angular.module("umbraco").controller('Umbraco.Dialogs.Template.SnippetController', + function($scope) { + $scope.type = $scope.dialogOptions.type; + $scope.section = { + name: "", + required: false + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/snippet.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/snippet.html new file mode 100644 index 0000000000..7b5187303e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/snippet.html @@ -0,0 +1,37 @@ +
+
+ +
+
Configure the section
+
+ + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html index f80fc0bdc2..02cc88a845 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html @@ -8,17 +8,16 @@

{{user.name}}

+ Umbraco version {{version}}

: {{remainingAuthSeconds | timespan}}

-
-
@@ -100,7 +99,5 @@
- - Umbraco version {{version}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index b67431cdc8..2bdbe4796b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper) { +function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); @@ -49,6 +49,7 @@ function contentPickerController($scope, dialogService, entityResource, editorSt //the default pre-values var defaultConfig = { multiPicker: false, + showOpenButton: false, showEditButton: false, showPathOnHover: false, startNode: { @@ -65,14 +66,17 @@ function contentPickerController($scope, dialogService, entityResource, editorSt //Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that! $scope.model.config.multiPicker = ($scope.model.config.multiPicker === "1" ? true : false); + $scope.model.config.showOpenButton = ($scope.model.config.showOpenButton === "1" ? true : false); $scope.model.config.showEditButton = ($scope.model.config.showEditButton === "1" ? true : false); $scope.model.config.showPathOnHover = ($scope.model.config.showPathOnHover === "1" ? true : false); - + var entityType = $scope.model.config.startNode.type === "member" ? "Member" : $scope.model.config.startNode.type === "media" ? "Media" : "Document"; + $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; + $scope.allowEditButton = entityType === "Document"; //the dialog options for the picker var dialogOptions = { @@ -146,6 +150,21 @@ function contentPickerController($scope, dialogService, entityResource, editorSt $scope.renderModel.splice(index, 1); angularHelper.getCurrentForm($scope).$setDirty(); }; + + $scope.showNode = function (index) { + var item = $scope.renderModel[index]; + var id = item.id; + var section = $scope.model.config.startNode.type.toLowerCase(); + + entityResource.getPath(id, entityType).then(function (path) { + navigationService.changeSection(section); + navigationService.showTree(section, { + tree: section, path: path, forceReload: false, activate: true + }); + var routePath = section + "/" + section + "/edit/" + id.toString(); + $location.path(routePath).search(""); + }); + } $scope.add = function (item) { var currIds = _.map($scope.renderModel, function (i) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 68487ceb7a..86674abfe7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -11,10 +11,12 @@ {{node.name}} - -
- Edit -
+ + +
+ Open + Edit +
@@ -34,11 +36,10 @@
You need to add at least {{model.config.minNumber}} items
- +
You can only have {{model.config.maxNumber}} items selected
-
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif new file mode 100644 index 0000000000..1a84493fe9 Binary files /dev/null and b/src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif differ diff --git a/src/Umbraco.Web.UI/Umbraco/js/install.loader.js b/src/Umbraco.Web.UI/Umbraco/js/install.loader.js index 869521ec7d..f61c9d2878 100644 --- a/src/Umbraco.Web.UI/Umbraco/js/install.loader.js +++ b/src/Umbraco.Web.UI/Umbraco/js/install.loader.js @@ -1,4 +1,5 @@ -LazyLoad.js( [ +try { + LazyLoad.js([ 'lib/jquery/jquery.min.js', /* 1.1.5 */ 'lib/angular/1.1.5/angular.min.js', @@ -13,5 +14,11 @@ LazyLoad.js( [ jQuery(document).ready(function () { angular.bootstrap(document, ['ngSanitize', 'umbraco.install', 'umbraco.directives.validation']); }); + }); +} +catch (err) { + if (err.message == 'LazyLoad is not defined') { + document.getElementById("feedback").style.display = "none"; + document.getElementById("missinglazyload").style.display = "block"; } -); \ No newline at end of file +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml b/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml index 8171aad9e2..666f6a5b5a 100644 --- a/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml +++ b/src/Umbraco.Web.UI/umbraco/Install/Views/Index.cshtml @@ -45,7 +45,20 @@

-

{{installer.feedback}}

+

{{installer.feedback}}

+ + +