Merge remote-tracking branch 'refs/remotes/umbraco/dev-v7' into dev-v7

This commit is contained in:
bjarnef
2015-11-25 20:13:15 +01:00
65 changed files with 1004 additions and 292 deletions

View File

@@ -1,4 +1,7 @@
namespace Umbraco.Core
using System;
using System.ComponentModel;
namespace Umbraco.Core
{
public static partial class Constants
{
@@ -15,6 +18,8 @@
/// <summary>
/// The auth cookie name
/// </summary>
[Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string AuthCookieName = "UMB_UCONTEXT";
}

View File

@@ -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);
}
/// <summary>
/// This clears the forms authentication cookie
/// </summary>
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
/// </summary>
/// <param name="response"></param>
[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 });
}
/// <summary>
/// This adds the forms authentication cookie for webapi since cookies are handled differently
/// </summary>
/// <param name="response"></param>
/// <param name="user"></param>
[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();
}
/// <summary>
/// Renews the Umbraco authentication ticket
/// This will force ticket renewal in the OWIN pipeline
/// </summary>
/// <param name="http"></param>
/// <returns></returns>
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;
}
/// <summary>
/// This will force ticket renewal in the OWIN pipeline
/// </summary>
/// <param name="http"></param>
/// <returns></returns>
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;
}
/// <summary>
@@ -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);
}
/// <summary>
/// Renews the forms authentication ticket & cookie
/// </summary>
/// <param name="http"></param>
/// <param name="cookieName"></param>
/// <param name="cookieDomain"></param>
/// <param name="minutesPersisted"></param>
/// <returns>true if there was a ticket to renew otherwise false if there was no ticket</returns>
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;
}
/// <summary>
/// Creates a custom FormsAuthentication ticket with the data specified
/// </summary>

View File

@@ -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 = "/"
});
}
/// <summary>
/// Ensures that the culture is set correctly for the current back office user
/// </summary>

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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<ILogger>(), Mock.Of<IProfiler>()));
var umbCtx = UmbracoContext.EnsureContext(
Mock.Of<HttpContextBase>(),
appCtx,
new Mock<WebSecurity>(null, null).Object,
Mock.Of<IUmbracoSettingsSection>(),
Enumerable.Empty<IUrlProvider>(),
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<ILogger>(), Mock.Of<IProfiler>()));
var umbCtx = UmbracoContext.EnsureContext(
Mock.Of<HttpContextBase>(),
appCtx,
new Mock<WebSecurity>(null, null).Object,
Mock.Of<IUmbracoSettingsSection>(),
Enumerable.Empty<IUrlProvider>(),
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<ILogger>(), Mock.Of<IProfiler>()));
var umbCtx = UmbracoContext.EnsureContext(
Mock.Of<HttpContextBase>(),
appCtx,
new Mock<WebSecurity>(null, null).Object,
Mock.Of<IUmbracoSettingsSection>(),
Enumerable.Empty<IUrlProvider>(),
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<ILogger>(), Mock.Of<IProfiler>()));
var umbCtx = UmbracoContext.EnsureContext(
Mock.Of<HttpContextBase>(),
appCtx,
new Mock<WebSecurity>(null, null).Object,
Mock.Of<IUmbracoSettingsSection>(),
Enumerable.Empty<IUrlProvider>(),
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<ActionResult> Index(RenderModel model)
{
return await Task.FromResult(base.Index(model));
}
}
}
}

View File

@@ -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;

View File

@@ -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<HttpRequestBase>();

View File

@@ -180,6 +180,7 @@
<Compile Include="Logging\AsyncRollingFileAppenderTest.cs" />
<Compile Include="Logging\DebugAppender.cs" />
<Compile Include="Logging\ParallelForwarderTest.cs" />
<Compile Include="Mvc\RenderIndexActionSelectorAttributeTests.cs" />
<Compile Include="Persistence\Repositories\AuditRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\DomainRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\PartialViewRepositoryTests.cs" />

View File

@@ -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"
}
}

View File

@@ -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',

View File

@@ -7,6 +7,7 @@ var app = angular.module('umbraco', [
'ngCookies',
'ngMobile',
'ngSanitize',
'ngCapsLock',
/*'ui.sortable',*/
'blueimp.fileupload',
'tmh.dynamicLocale'

View File

@@ -7,6 +7,7 @@ var app = angular.module('umbraco', [
'ngCookies',
'ngSanitize',
'ngMobile',
'ngCapsLock',
'blueimp.fileupload',
'tmh.dynamicLocale'
]);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -14,6 +14,7 @@
@grayDarker: #222;
@grayDark: #343434;
@gray: #555;
@grayMed: #999;
@grayLight: #d9d9d9;
@grayLighter: #f8f8f8;
@white: #fff;

View File

@@ -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',

View File

@@ -41,6 +41,7 @@
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
<input type="password" ng-model="password" name="password" class="input-xlarge" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" />
<p class="caps-lock-alert" ng-show="isCapsLockOn"><i class="icon-alert"></i> <localize key="login_capsLockIsOn">Caps Lock is on</localize></p>
</div>
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>

View File

@@ -62,18 +62,16 @@
<div class="umb-panel umb-dialogs-mediapicker browser" ng-hide="target" ng-style="{opacity: dropping == true ? '0.25' : '1.0'}">
<div class="umb-panel-header">
<div class="umb-el-wrap umb-panel-buttons">
<div class="span5">
<div class="form-search">
<i class="icon-search"></i>
<input type="text"
ng-model="searchTerm"
class="umb-search-field search-query"
placeholder="Filter...">
</div>
<div class="umb-el-wrap umb-panel-buttons umb-mediapicker-upload">
<div class="form-search">
<i class="icon-search"></i>
<input type="text"
ng-model="searchTerm"
class="umb-search-field search-query"
placeholder="Filter...">
</div>
<div class="pull-right">
<div class="upload-button">
<span class="btn fileinput-button" ng-class="{disabled: disabled}">
<i class="icon-page-up"></i>
<input type="file" name="files[]" multiple ng-disabled="disabled" class="uploader">

View File

@@ -0,0 +1,8 @@
angular.module("umbraco").controller('Umbraco.Dialogs.Template.SnippetController',
function($scope) {
$scope.type = $scope.dialogOptions.type;
$scope.section = {
name: "",
required: false
};
});

View File

@@ -0,0 +1,37 @@
<form novalidate name="contentForm"
ng-controller="Umbraco.Dialogs.Template.SnippetController"
ng-submit="close()"
val-form-manager>
<div class="umb-panel">
<div class="umb-panel-header">
<div class="panel-dialog--header" style="border-bottom: 1px solid whitesmoke">Configure the section</div>
</div>
<div class="umb-panel-body with-footer umb-querybuilder">
<label>
<div class="input-label--title">
Section name:
</div>
<input type="text" ng-model="section.name"/>
</label>
<label ng-show="type === 'rendersection'">
<input style="margin: 0;" type="checkbox" ng-checked="section.required" ng-model="section.required"/>
<div class="input-label--small">
<localize key="general_required">Required</localize>
</div>
</label>
</div>
<div class="umb-panel-footer">
<div class="umb-el-wrap umb-panel-buttons">
<div class="btn-toolbar umb-btn-toolbar pull-right">
<a href ng-click="close()" class="btn btn-link">
<localize key="general_close">Close</localize>
</a>
<a href ng-click="submit(section)" class="btn btn-primary">Insert</a>
</div>
</div>
</div>
</div>
</form>

View File

@@ -8,17 +8,16 @@
</div>
</div>
<h1 class="headline">{{user.name}}</h1>
<small class="umb-version">Umbraco version {{version}}</small>
<p class="muted">
<small>
<localize key="user_sessionExpires" />: {{remainingAuthSeconds | timespan}}
</small>
</p>
</div>
<div class="umb-panel-body umb-scrollable">
<div class="tab-content umb-control-group">
<div class="umb-pane">
<h5><localize key="user_yourProfile" /></h5>
@@ -100,7 +99,5 @@
</div>
</div>
<small class="umb-version">Umbraco version {{version}}</small>
</div>
</div>

View File

@@ -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) {

View File

@@ -11,10 +11,12 @@
<i class="icon icon-delete red hover-show"></i>
<i class="{{node.icon}} hover-hide"></i>
{{node.name}}
</a>
<div ng-if="!dialogEditor && model.config.showEditButton">
<small><a href umb-launch-mini-editor="node"><localize key="edit">Edit</localize></a></small>
</div>
</a>
<div ng-if="!dialogEditor && ((model.config.showOpenButton && allowOpenButton) || (model.config.showEditButton && allowEditButton))">
<small ng-if="model.config.showOpenButton && allowOpenButton"><a href ng-click="showNode($index)"><localize key="open">Open</localize></a></small>
<small ng-if="model.config.showEditButton && allowEditButton"><a href umb-launch-mini-editor="node"><localize key="edit">Edit</localize></a></small>
</div>
</li>
</ul>
@@ -34,11 +36,10 @@
<div class="help-inline" val-msg-for="minCount" val-toggle-msg="minCount">
You need to add at least {{model.config.minNumber}} items
</div>
<div class="help-inline" val-msg-for="maxCount" val-toggle-msg="maxCount">
You can only have {{model.config.maxNumber}} items selected
</div>
</ng-form>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -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";
}
);
}

View File

@@ -45,7 +45,20 @@
<p ng-bind-html-unsafe="installer.fact"></p>
</div>
<h3 ng-cloak ng-animate="'fade'" id="feedback" ng-show="installer.feedback">{{installer.feedback}}</h3>
<h3 ng-cloak ng-animate="'fade'" id="feedback" ng-show="installer.feedback">{{installer.feedback}}</h3>
<div id="missinglazyload" style="display: none;">
<h3>There has been a problem with the build.</h3>
<p>This might be because you could be offline or on a slow connection. Please try the following steps</p>
<ol>
<li>Make sure you have <a href="https://nodejs.org" target="_blank">Node.js</a> installed.</li>
<li>Open command prompt and cd to \src\Umbraco.Web.UI.Client.</li>
<li>Check to see if \src\Umbraco.Web.UI.Client\node_modules folder exists (this could be hidden); if so delete it.</li>
<li>Run npm install; if successfull the node_modules folder should be created in the Umbraco.Web.UI.Client directory.</li>
<li>Run build\Build.bat.</li>
</ol>
</div>
<script type="text/javascript">
var Umbraco = {};
Umbraco.Sys = {};

View File

@@ -490,7 +490,6 @@
<key alias="renewSession">Forny for at gemme dine ændringer</key>
</area>
<area alias="login">
<key alias="greeting0">Så er det søndag!</key>
<key alias="greeting1">Smil, det er mandag!</key>
<key alias="greeting2">Hurra, det er tirsdag!</key>
@@ -498,12 +497,9 @@
<key alias="greeting4">Glædelig torsdag!</key>
<key alias="greeting5">Endelig fredag!</key>
<key alias="greeting6">Glædelig lørdag</key>
<key alias="instruction">indtast brugernavn og kodeord</key>
<key alias="timeout">Din session er udløbet</key>
<key alias="capsLockIsOn">Caps Lock er slået til</key>
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.com</a></p> ]]></key>
</area>
<area alias="main">

View File

@@ -531,7 +531,8 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die
<key alias="greeting6">Wunderbaren sonnigen Samstag</key>
<key alias="instruction">Hier anmelden:</key>
<key alias="timeout">Die Sitzung ist abgelaufen</key>
<key alias="bottomText">&lt;p style="text-align:right;"&gt;&amp;copy; 2001 - %0% &lt;br /&gt;&lt;a href="http://umbraco.com" style="text-decoration: none" target="_blank"&gt;umbraco.org&lt;/a&gt;&lt;/p&gt; </key>
<key alias="capsLockIsOn">Caps Lock ist aktiviert</key>
<key alias="bottomText">&lt;p style="text-align:right;"&gt;&amp;copy; 2001 - %0% &lt;br /&gt;&lt;a href="http://umbraco.com" style="text-decoration: none" target="_blank"&gt;umbraco.org&lt;/a&gt;&lt;/p&gt; </key>
</area>
<area alias="main">
<key alias="dashboard">Armaturenbrett</key>

View File

@@ -561,6 +561,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="greeting6">Happy Caturday</key>
<key alias="instruction">Log in below</key>
<key alias="timeout">Session timed out</key>
<key alias="capsLockIsOn">Caps Lock is on</key>
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">Umbraco.com</a></p> ]]></key>
</area>
<area alias="main">

View File

@@ -562,6 +562,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="greeting6">Happy Caturday</key>
<key alias="instruction">Log in below</key>
<key alias="timeout">Session timed out</key>
<key alias="capsLockIsOn">Caps Lock is on</key>
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">Umbraco.com</a></p> ]]></key>
</area>
<area alias="main">

View File

@@ -248,29 +248,33 @@ namespace Umbraco.Web.UI.Umbraco.Settings.Views
/// </summary>
private void InitializeEditorForTemplate()
{
//TODO: implement content placeholders, etc... just like we had in v5
//Panel1.Menu.InsertSplitter();
//TODO: implement content placeholders, etc... just like we had in v5
//MenuIconI umbContainer = Panel1.Menu.NewIcon();
//umbContainer.ImageURL = UmbracoPath + "/images/editor/masterpagePlaceHolder.gif";
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
//umbContainer.OnClickCommand =
// ClientTools.Scripts.OpenModalWindow(
// IOHelper.ResolveUrl(SystemDirectories.Umbraco) +
// "/dialogs/insertMasterpagePlaceholder.aspx?&id=" + _template.Id,
// ui.Text("template", "insertContentAreaPlaceHolder"), 470, 320);
editorSource.Menu.InsertSplitter();
//MenuIconI umbContent = Panel1.Menu.NewIcon();
//umbContent.ImageURL = UmbracoPath + "/images/editor/masterpageContent.gif";
//umbContent.AltText = ui.Text("template", "insertContentArea");
//umbContent.OnClickCommand =
// ClientTools.Scripts.OpenModalWindow(
// IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/dialogs/insertMasterpageContent.aspx?id=" +
// _template.Id, ui.Text("template", "insertContentArea"), 470, 300);
}
MenuIconI umbRenderBody = editorSource.Menu.NewIcon();
umbRenderBody.ImageURL = UmbracoPath + "/images/editor/renderbody.gif";
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
umbRenderBody.AltText = "Insert @RenderBody()";
}
umbRenderBody.OnClickCommand = "editViewEditor.insertRenderBody()";
MenuIconI umbSection = editorSource.Menu.NewIcon();
umbSection.ImageURL = UmbracoPath + "/images/editor/masterpagePlaceHolder.gif";
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
umbSection.AltText = "Insert Section";
umbSection.OnClickCommand = "editViewEditor.openSnippetModal('section')";
MenuIconI umbRenderSection = editorSource.Menu.NewIcon();
umbRenderSection.ImageURL = UmbracoPath + "/images/editor/masterpageContent.gif";
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
umbRenderSection.AltText = "Insert @RenderSection";
umbRenderSection.OnClickCommand = "editViewEditor.openSnippetModal('rendersection')";
}
}
}

View File

@@ -36,6 +36,10 @@
UmbEditor.Insert("@Umbraco.RenderMacro(\"" + alias + "\")", "", this._opts.codeEditorElementId);
},
insertRenderBody: function() {
UmbEditor.Insert("@RenderBody()", "", this._opts.codeEditorElementId);
},
openMacroModal: function (alias) {
/// <summary>callback used to display the modal dialog to insert a macro with parameters</summary>
@@ -54,6 +58,37 @@
});
},
openSnippetModal: function (type) {
/// <summary>callback used to display the modal dialog to insert a macro with parameters</summary>
var self = this;
UmbClientMgr.openAngularModalWindow({
template: "views/common/dialogs/template/snippet.html",
callback: function (data) {
var code = "";
if (type === 'section') {
code = "\n@section " + data.name + "{\n";
code += "<!-- Content here -->\n" +
"}\n";
}
if (type === 'rendersection') {
if (data.required) {
code = "\n@RenderSection(\"" + data.name + "\", true);\n";
} else {
code = "\n@RenderSection(\"" + data.name + "\" false);\n";
}
}
UmbEditor.Insert(code, '', self._opts.codeEditorElementId);
},
type: type
});
},
openQueryModal: function () {
/// <summary>callback used to display the modal dialog to insert a macro with parameters</summary>

View File

@@ -177,19 +177,14 @@ namespace Umbraco.Web.Editors
//get the user
var user = Security.GetBackOfficeUser(loginModel.Username);
var userDetail = Mapper.Map<UserDetail>(user);
//update the userDetail and set their remaining seconds
userDetail.SecondsUntilTimeout = TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
//create a response with the userDetail object
var response = Request.CreateResponse(HttpStatusCode.OK, userDetail);
//set the response cookies with the ticket (NOTE: This needs to be done with the custom webapi extension because
// we cannot mix HttpContext.Response.Cookies and the way WebApi/Owin work)
var ticket = response.UmbracoLoginWebApi(user);
//This ensure the current principal is set, otherwise any logic executing after this wouldn't actually be authenticated
http.Result.AuthenticateCurrentRequest(ticket, false);
//update the userDetail and set their remaining seconds
userDetail.SecondsUntilTimeout = ticket.GetRemainingAuthSeconds();
//ensure the user is set for the current request
Request.SetPrincipalForRequest(user);
return response;
@@ -241,11 +236,12 @@ namespace Umbraco.Web.Editors
/// Logs the current user out
/// </summary>
/// <returns></returns>
[UmbracoBackOfficeLogout]
[ClearAngularAntiForgeryToken]
[ValidateAngularAntiForgeryToken]
public HttpResponseMessage PostLogout()
{
Request.TryGetOwinContext().Result.Authentication.SignOut();
Logger.Info<AuthenticationController>("User {0} from IP address {1} has logged out",
() => User.Identity == null ? "UNKNOWN" : User.Identity.Name,
() => TryGetOwinContext().Result.Request.RemoteIpAddress);

View File

@@ -3,6 +3,7 @@ using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using Microsoft.Owin;
using Umbraco.Core;
namespace Umbraco.Web
@@ -62,6 +63,16 @@ namespace Umbraco.Web
return request.Cookies[Constants.Web.PreviewCookieName] != null;
}
/// <summary>
/// Does a preview cookie exist ?
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public static bool HasPreviewCookie(this IOwinRequest request)
{
return request.Cookies[Constants.Web.PreviewCookieName] != null;
}
/// <summary>
/// Does a cookie exist with the specified key ?
/// </summary>

View File

@@ -43,14 +43,8 @@ namespace Umbraco.Web.Install.InstallSteps
var security = new WebSecurity(_httpContext, _applicationContext);
security.PerformLogin(0);
////Clear the auth cookie - this is required so that the login screen is displayed after upgrade and so the
//// csrf anti-forgery tokens are created, otherwise there will just be JS errors if the user has an old
//// login token from a previous version when we didn't have csrf tokens in place
//var security = new WebSecurity(new HttpContextWrapper(Context), ApplicationContext.Current);
//security.ClearCurrentLogin();
//reports the ended install
InstallHelper ih = new InstallHelper(UmbracoContext.Current);
var ih = new InstallHelper(UmbracoContext.Current);
ih.InstallStatus(true, "");
return null;

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web.Mvc;
@@ -36,6 +37,8 @@ namespace Umbraco.Web.Mvc
/// <summary>
/// Returns an instance of the default controller instance.
/// </summary>
[Obsolete("This method will be removed in future versions and should not be used to resolve a controller instance, the IControllerFactory is used for that purpose")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IRenderMvcController GetControllerInstance()
{
//try the dependency resolver, then the activator
@@ -64,9 +67,9 @@ namespace Umbraco.Web.Mvc
/// <param name="type"></param>
private void ValidateType(Type type)
{
if (TypeHelper.IsTypeAssignableFrom<IRenderMvcController>(type) == false)
if (TypeHelper.IsTypeAssignableFrom<IRenderController>(type) == false)
{
throw new InvalidOperationException("The Type specified (" + type + ") is not of type " + typeof(IRenderMvcController));
throw new InvalidOperationException("The Type specified (" + type + ") is not of type " + typeof (IRenderController));
}
}

View File

@@ -0,0 +1,12 @@
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A marker interface to designate that a controller will be used for Umbraco front-end requests and/or route hijacking
/// </summary>
public interface IRenderController : IController
{
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Web.Http.Filters;
using System.Web.Mvc;
using System.Web.Routing;
using System.Windows.Forms;
using Umbraco.Web.Models;
@@ -8,7 +10,7 @@ namespace Umbraco.Web.Mvc
/// <summary>
/// The interface that must be implemented for a controller to be designated to execute for route hijacking
/// </summary>
public interface IRenderMvcController : IController
public interface IRenderMvcController : IRenderController
{
/// <summary>
/// The default action to render the front-end view

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
@@ -12,6 +13,8 @@ namespace Umbraco.Web.Mvc
/// </summary>
public class RenderActionInvoker : AsyncControllerActionInvoker
{
/// <summary>
/// Ensures that if an action for the Template name is not explicitly defined by a user, that the 'Index' action will execute
/// </summary>
@@ -26,14 +29,14 @@ namespace Umbraco.Web.Mvc
//now we need to check if it exists, if not we need to return the Index by default
if (ad == null)
{
//check if the controller is an instance of IRenderMvcController
if (controllerContext.Controller is IRenderMvcController)
//check if the controller is an instance of IRenderController and find the index
if (controllerContext.Controller is IRenderController)
{
return controllerDescriptor.FindAction(controllerContext, "Index");
}
}
return ad;
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A custom ActionMethodSelector which will ensure that the RenderMvcController.Index(RenderModel model) action will be executed
/// if the
/// </summary>
internal class RenderIndexActionSelectorAttribute : ActionMethodSelectorAttribute
{
private static readonly ConcurrentDictionary<Type, ReflectedControllerDescriptor> ControllerDescCache = new ConcurrentDictionary<Type, ReflectedControllerDescriptor>();
/// <summary>
/// Determines whether the action method selection is valid for the specified controller context.
/// </summary>
/// <returns>
/// true if the action method selection is valid for the specified controller context; otherwise, false.
/// </returns>
/// <param name="controllerContext">The controller context.</param><param name="methodInfo">Information about the action method.</param>
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var currType = methodInfo.ReflectedType;
var baseType = methodInfo.DeclaringType;
//It's the same type, so this must be the Index action to use
if (currType == baseType) return true;
if (currType == null) return false;
var controllerDesc = ControllerDescCache.GetOrAdd(currType, type => new ReflectedControllerDescriptor(currType));
var actions = controllerDesc.GetCanonicalActions();
//If there are more than one Index action for this controller, then
// this base class one should not be matched
return actions.Count(x => x.ActionName == "Index") <= 1;
}
}
}

View File

@@ -1,22 +1,17 @@
using System;
using System.IO;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A controller to render front-end requests
/// The default controller to render front-end requests
/// </summary>
[PreRenderViewActionFilter]
[PreRenderViewActionFilter]
public class RenderMvcController : UmbracoController, IRenderMvcController
{
@@ -64,7 +59,7 @@ namespace Umbraco.Web.Mvc
{
if (_publishedContentRequest != null)
return _publishedContentRequest;
if (!RouteData.DataTokens.ContainsKey("umbraco-doc-request"))
if (RouteData.DataTokens.ContainsKey("umbraco-doc-request") == false)
{
throw new InvalidOperationException("DataTokens must contain an 'umbraco-doc-request' key with a PublishedContentRequest object");
}
@@ -111,10 +106,10 @@ namespace Umbraco.Web.Mvc
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[RenderIndexActionSelector]
public virtual ActionResult Index(RenderModel model)
{
return CurrentTemplate(model);
}
}
}

View File

@@ -310,8 +310,8 @@ namespace Umbraco.Web.Mvc
//check if that controller exists
if (controllerType != null)
{
//ensure the controller is of type 'IRenderMvcController' and ControllerBase
if (TypeHelper.IsTypeAssignableFrom<IRenderMvcController>(controllerType)
//ensure the controller is of type IRenderMvcController and ControllerBase
if (TypeHelper.IsTypeAssignableFrom<IRenderController>(controllerType)
&& TypeHelper.IsTypeAssignableFrom<ControllerBase>(controllerType))
{
//set the controller and name to the custom one
@@ -328,7 +328,7 @@ namespace Umbraco.Web.Mvc
"The current Document Type {0} matches a locally declared controller of type {1}. Custom Controllers for Umbraco routing must implement '{2}' and inherit from '{3}'.",
() => publishedContentRequest.PublishedContent.DocumentTypeAlias,
() => controllerType.FullName,
() => typeof(IRenderMvcController).FullName,
() => typeof(IRenderController).FullName,
() => typeof(ControllerBase).FullName);
//we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults

View File

@@ -238,7 +238,7 @@ namespace Umbraco.Web.Mvc
{
if (UmbracoContext.Current.IsDebug || UmbracoContext.Current.InPreviewMode)
{
var text = value.ToString().ToLowerInvariant();
var text = value.ToString();
var pos = text.IndexOf("</body>", StringComparison.InvariantCultureIgnoreCase);
if (pos > -1)
@@ -294,4 +294,4 @@ namespace Umbraco.Web.Mvc
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
}
}
}

View File

@@ -11,11 +11,12 @@ namespace Umbraco.Web.PropertyEditors
public ContentPickerPropertyEditor()
{
_internalPreValues = new Dictionary<string, object>
{
{"showEditButton", "0"},
{"startNodeId", "-1"},
{"showPathOnHover", "0"}
};
{
{"startNodeId", "-1"},
{"showOpenButton", "0"},
{"showEditButton", "0"},
{"showPathOnHover", "0"}
};
}
private IDictionary<string, object> _internalPreValues;
@@ -32,6 +33,9 @@ namespace Umbraco.Web.PropertyEditors
internal class ContentPickerPreValueEditor : PreValueEditor
{
[PreValueField("showOpenButton", "Show open button", "boolean")]
public string ShowOpenButton { get; set; }
[PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")]
public string ShowEditButton { get; set; }

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
@@ -15,10 +11,12 @@ namespace Umbraco.Web.PropertyEditors
public MultiNodeTreePickerPropertyEditor()
{
_internalPreValues = new Dictionary<string, object>
{
{"multiPicker", "1"},
{"showEditButton", "0"}
};
{
{"multiPicker", "1"},
{"showOpenButton", "0"},
{"showEditButton", "0"},
{"showPathOnHover", "0"}
};
}
protected override PreValueEditor CreatePreValueEditor()
@@ -47,9 +45,16 @@ namespace Umbraco.Web.PropertyEditors
[PreValueField("maxNumber", "Maximum number of items", "number")]
public string MaxNumber { get; set; }
[PreValueField("showOpenButton", "Show open button", "boolean")]
public string ShowOpenButton { get; set; }
[PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")]
public string ShowEditButton { get; set; }
[PreValueField("showPathOnHover", "Show path when hovering items", "boolean")]
public bool ShowPathOnHover { get; set; }
/// <summary>
/// This ensures the multiPicker pre-val is set based on the maxNumber of nodes set
/// </summary>

View File

@@ -155,7 +155,27 @@ namespace Umbraco.Web.Security.Identity
UmbracoConfig.For.UmbracoSettings().Security,
app.CreateLogger<GetUserSecondsMiddleWare>());
app.UseCookieAuthentication(authOptions);
app.UseUmbracoBackOfficeCookieAuthentication(authOptions);
return app;
}
internal static IAppBuilder UseUmbracoBackOfficeCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options)
{
if (app == null)
{
throw new ArgumentNullException("app");
}
//First the normal cookie middleware
app.Use(typeof(CookieAuthenticationMiddleware), app, options);
app.UseStageMarker(PipelineStage.Authenticate);
//Then our custom middlewares
app.Use(typeof(ForceRenewalCookieAuthenticationMiddleware), app, options);
app.UseStageMarker(PipelineStage.Authenticate);
app.Use(typeof(FixWindowsAuthMiddlware));
app.UseStageMarker(PipelineStage.Authenticate);
return app;
}

View File

@@ -1,6 +1,9 @@
using Microsoft.Owin;
using System;
using System.Web;
using Microsoft.Owin;
using Microsoft.Owin.Infrastructure;
using Umbraco.Core;
using Umbraco.Core.IO;
namespace Umbraco.Web.Security.Identity
{
@@ -33,8 +36,8 @@ namespace Umbraco.Web.Security.Identity
return null;
}
return UmbracoModule.ShouldAuthenticateRequest(
context.HttpContextFromOwinContext().Request,
return ShouldAuthenticateRequest(
context,
_umbracoContextAccessor.Value.OriginalRequestUrl) == false
//Don't auth request, don't return a cookie
? null
@@ -42,5 +45,40 @@ namespace Umbraco.Web.Security.Identity
: base.GetRequestCookie(context, key);
}
/// <summary>
/// Determines if we should authenticate the request
/// </summary>
/// <param name="ctx"></param>
/// <param name="originalRequestUrl"></param>
/// <returns></returns>
/// <remarks>
/// We auth the request when:
/// * it is a back office request
/// * it is an installer request
/// * it is a /base request
/// * it is a preview request
/// </remarks>
internal static bool ShouldAuthenticateRequest(IOwinContext ctx, Uri originalRequestUrl)
{
var request = ctx.Request;
var httpCtx = ctx.TryGetHttpContext();
if (//check the explicit flag
ctx.Get<bool?>("umbraco-force-auth") != null
|| (httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null)
//check back office
|| request.Uri.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)
//check installer
|| request.Uri.IsInstallerRequest()
//detect in preview
|| (request.HasPreviewCookie() && request.Uri != null && request.Uri.AbsolutePath.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)) == false)
//check for base
|| BaseRest.BaseRestHandler.IsBaseRestRequest(originalRequestUrl))
{
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Owin;
using Umbraco.Core;
using Umbraco.Core.Security;
namespace Umbraco.Web.Security.Identity
{
/// <summary>
/// This is used to inspect the request to see if 2 x identities are assigned: A windows one and a back office one.
/// When this is the case, it means that auth has executed for Windows & auth has executed for our back office cookie
/// handler and now two identities have been assigned. Unfortunately, at some stage in the pipeline - I'm pretty sure
/// it's the Role Provider Module - it again changes the user's Principal to a RolePrincipal and discards the second
/// Identity which is the Back office identity thus preventing a user from accessing the back office... it's very annoying.
///
/// To fix this, we re-set the user Principal to only have a single identity: the back office one, since we know this is
/// for a back office request.
/// </summary>
internal class FixWindowsAuthMiddlware : OwinMiddleware
{
public FixWindowsAuthMiddlware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
if (context.Request.Uri.IsClientSideRequest() == false)
{
var claimsPrincipal = context.Request.User as ClaimsPrincipal;
if (claimsPrincipal != null
&& claimsPrincipal.Identities.Count() > 1
&& claimsPrincipal.Identities.Any(x => x is WindowsIdentity)
&& claimsPrincipal.Identities.Any(x => x is UmbracoBackOfficeIdentity))
{
var backOfficeIdentity = claimsPrincipal.Identities.First(x => x is UmbracoBackOfficeIdentity);
if (backOfficeIdentity.IsAuthenticated)
{
context.Request.User = new ClaimsPrincipal(backOfficeIdentity);
}
}
}
if (Next != null)
{
await Next.Invoke(context);
}
}
}
}

View File

@@ -0,0 +1,112 @@
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Infrastructure;
namespace Umbraco.Web.Security.Identity
{
/// <summary>
/// If a flag is set on the context to force renew the ticket, this will do it
/// </summary>
internal class ForceRenewalCookieAuthenticationHandler : AuthenticationHandler<CookieAuthenticationOptions>
{
/// <summary>
/// This handler doesn't actually do any auth so we return null;
/// </summary>
/// <returns></returns>
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult((AuthenticationTicket)null);
}
/// <summary>
/// Gets the ticket from the request
/// </summary>
/// <returns></returns>
private AuthenticationTicket GetTicket()
{
var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
if (string.IsNullOrWhiteSpace(cookie))
{
return null;
}
var ticket = Options.TicketDataFormat.Unprotect(cookie);
if (ticket == null)
{
return null;
}
return ticket;
}
/// <summary>
/// This will check if the token exists in the request to force renewal
/// </summary>
/// <returns></returns>
protected override Task ApplyResponseGrantAsync()
{
//Now we need to check if we should force renew this based on a flag in the context
var httpCtx = Context.TryGetHttpContext();
//check for the special flag in either the owin or http context
var shouldRenew = Context.Get<bool?>("umbraco-force-auth") != null || (httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null);
if (shouldRenew)
{
var signin = Helper.LookupSignIn(Options.AuthenticationType);
var shouldSignin = signin != null;
var signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode);
var shouldSignout = signout != null;
//we don't care about the sign in/sign out scenario, only renewal
if (shouldSignin == false && shouldSignout == false)
{
//get the ticket
var model = GetTicket();
if (model != null)
{
var currentUtc = Options.SystemClock.UtcNow;
var issuedUtc = model.Properties.IssuedUtc;
var expiresUtc = model.Properties.ExpiresUtc;
if (expiresUtc.HasValue && issuedUtc.HasValue)
{
//renew the date/times
model.Properties.IssuedUtc = currentUtc;
var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
model.Properties.ExpiresUtc = currentUtc.Add(timeSpan);
//now save back all the required cookie details
var cookieValue = Options.TicketDataFormat.Protect(model);
var cookieOptions = new CookieOptions
{
Domain = Options.CookieDomain,
HttpOnly = Options.CookieHttpOnly,
Path = Options.CookiePath ?? "/",
};
if (Options.CookieSecure == CookieSecureOption.SameAsRequest)
{
cookieOptions.Secure = Request.IsSecure;
}
else
{
cookieOptions.Secure = Options.CookieSecure == CookieSecureOption.Always;
}
if (model.Properties.IsPersistent)
{
cookieOptions.Expires = model.Properties.ExpiresUtc.Value.ToUniversalTime().DateTime;
}
Options.CookieManager.AppendResponseCookie(
Context,
Options.CookieName,
cookieValue,
cookieOptions);
}
}
}
}
return Task.FromResult(0);
}
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Infrastructure;
using Owin;
namespace Umbraco.Web.Security.Identity
{
/// <summary>
/// This middleware is used simply to force renew the auth ticket if a flag to do so is found in the request
/// </summary>
internal class ForceRenewalCookieAuthenticationMiddleware : CookieAuthenticationMiddleware
{
public ForceRenewalCookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) : base(next, app, options)
{
}
protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler()
{
return new ForceRenewalCookieAuthenticationHandler();
}
}
}

View File

@@ -76,15 +76,15 @@ namespace Umbraco.Web.Security.Identity
{
var utcNow = DateTime.Now.ToUniversalTime();
ticket.Properties.IssuedUtc = utcNow;
ticket.Properties.ExpiresUtc = utcNow.AddMinutes(_authOptions.LoginTimeoutMinutes);
ticket.Properties.ExpiresUtc = utcNow.AddMinutes(30);
var cookieValue = _authOptions.TicketDataFormat.Protect(ticket);
var cookieOptions = new CookieOptions
{
Path = "/",
Domain = _authOptions.CookieDomain ?? "FALSE",
Expires = DateTime.Now.AddMinutes(_authOptions.LoginTimeoutMinutes),
Domain = _authOptions.CookieDomain ?? null,
Expires = DateTime.Now.AddMinutes(30),
HttpOnly = true,
Secure = _authOptions.CookieSecure == CookieSecureOption.Always
|| (_authOptions.CookieSecure == CookieSecureOption.SameAsRequest && request.Uri.Scheme.InvariantEquals("https")),
@@ -98,13 +98,13 @@ namespace Umbraco.Web.Security.Identity
remainingSeconds = (ticket.Properties.ExpiresUtc.Value - DateTime.Now.ToUniversalTime()).TotalSeconds;
}
else if (remainingSeconds <=30)
else if (remainingSeconds <= 30)
{
//NOTE: We are using 30 seconds because that is what is coded into angular to force logout to give some headway in
// the timeout process.
_logger.WriteCore(TraceEventType.Information, 0,
string.Format("User logged will be logged out due to timeout: {0}, IP Address: {1}", ticket.Identity.Name, request.RemoteIpAddress),
string.Format("User logged will be logged out due to timeout: {0}, IP Address: {1}", ticket.Identity.Name, request.RemoteIpAddress),
null, null);
}

View File

@@ -36,7 +36,7 @@ namespace Umbraco.Web.Security.Identity
}
SlidingExpiration = true;
ExpireTimeSpan = TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes);
ExpireTimeSpan = TimeSpan.FromMinutes(LoginTimeoutMinutes);
CookieDomain = securitySection.AuthCookieDomain;
CookieName = securitySection.AuthCookieName;
CookieHttpOnly = true;

View File

@@ -1,7 +1,8 @@
using System.Web;
using System.Web;
using Microsoft.Owin;
using Umbraco.Core;
namespace Umbraco.Web.Security.Identity
namespace Umbraco.Web.Security
{
internal static class OwinExtensions
{
@@ -11,9 +12,10 @@ namespace Umbraco.Web.Security.Identity
/// </summary>
/// <param name="owinContext"></param>
/// <returns></returns>
internal static HttpContextBase HttpContextFromOwinContext(this IOwinContext owinContext)
internal static Attempt<HttpContextBase> TryGetHttpContext(this IOwinContext owinContext)
{
return owinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
var ctx = owinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
return ctx == null ? Attempt<HttpContextBase>.Fail() : Attempt.Succeed(ctx);
}
}

View File

@@ -0,0 +1,74 @@
using System.Net.Http;
using System.Security.Claims;
using System.Security.Principal;
using System.ServiceModel.Channels;
using System.Threading;
using System.Web;
using AutoMapper;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Security
{
internal static class WebAuthExtensions
{
/// <summary>
/// This will set a an authenticated IPrincipal to the current request given the IUser object
/// </summary>
/// <param name="request"></param>
/// <param name="user"></param>
/// <returns></returns>
internal static IPrincipal SetPrincipalForRequest(this HttpRequestMessage request, IUser user)
{
var principal = new ClaimsPrincipal(
new UmbracoBackOfficeIdentity(
new ClaimsIdentity(),
Mapper.Map<UserData>(user)));
//It is actually not good enough to set this on the current app Context and the thread, it also needs
// to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually
// an underlying fault of asp.net not propogating the User correctly.
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
var http = request.TryGetHttpContext();
if (http)
{
http.Result.User = principal;
}
Thread.CurrentPrincipal = principal;
//For WebAPI
request.SetUserPrincipal(principal);
return principal;
}
/// <summary>
/// This will set a an authenticated IPrincipal to the current request given the IUser object
/// </summary>
/// <param name="httpContext"></param>
/// <param name="user"></param>
/// <returns></returns>
internal static IPrincipal SetPrincipalForRequest(this HttpContextBase httpContext, IUser user)
{
var principal = new ClaimsPrincipal(
new UmbracoBackOfficeIdentity(
new ClaimsIdentity(),
Mapper.Map<UserData>(user)));
//It is actually not good enough to set this on the current app Context and the thread, it also needs
// to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually
// an underlying fault of asp.net not propogating the User correctly.
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
httpContext.User = principal;
Thread.CurrentPrincipal = principal;
return principal;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
using System.Web.Security;
@@ -8,8 +9,9 @@ using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using umbraco;
using Microsoft.AspNet.Identity.Owin;
using umbraco.businesslogic.Exceptions;
using Umbraco.Web.Models.ContentEditing;
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
using User = umbraco.BusinessLogic.User;
@@ -84,32 +86,27 @@ namespace Umbraco.Web.Security
/// <returns>returns the number of seconds until their session times out</returns>
public virtual double PerformLogin(int userId)
{
var owinCtx = _httpContext.GetOwinContext();
var user = _applicationContext.Services.UserService.GetUserById(userId);
return PerformLogin(user).GetRemainingAuthSeconds();
var userDetail = Mapper.Map<UserDetail>(user);
//update the userDetail and set their remaining seconds
userDetail.SecondsUntilTimeout = TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
var principal = _httpContext.SetPrincipalForRequest(user);
owinCtx.Authentication.SignIn((UmbracoBackOfficeIdentity)principal.Identity);
return TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
}
/// <summary>
/// Logs the user in
/// </summary>
/// <param name="user"></param>
/// <returns>returns the Forms Auth ticket created which is used to log them in</returns>
[Obsolete("This method should not be used, login is performed by the OWIN pipeline, use the overload that returns double and accepts a UserId instead")]
public virtual FormsAuthenticationTicket PerformLogin(IUser user)
{
//clear the external cookie - we do this without owin context because we're writing cookies directly to httpcontext
// and cookie handling is different with httpcontext vs webapi and owin, normally we'd do:
//_httpContext.GetOwinContext().Authentication.SignOut(Constants.Security.BackOfficeExternalAuthenticationType);
var externalLoginCookie = _httpContext.Request.Cookies.Get(Constants.Security.BackOfficeExternalCookieName);
if (externalLoginCookie != null)
{
externalLoginCookie.Expires = DateTime.Now.AddYears(-1);
_httpContext.Response.Cookies.Set(externalLoginCookie);
}
var ticket = _httpContext.CreateUmbracoAuthTicket(Mapper.Map<UserData>(user));
LogHelper.Info<WebSecurity>("User Id: {0} logged in", () => user.Id);
return ticket;
}
@@ -119,6 +116,7 @@ namespace Umbraco.Web.Security
public virtual void ClearCurrentLogin()
{
_httpContext.UmbracoLogout();
_httpContext.GetOwinContext().Authentication.SignOut();
}
/// <summary>

View File

@@ -9,6 +9,8 @@
'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',

View File

@@ -304,15 +304,22 @@
<Compile Include="Media\EmbedProviders\Flickr.cs" />
<Compile Include="Models\ContentEditing\SimpleNotificationModel.cs" />
<Compile Include="Models\PublishedContentWithKeyBase.cs" />
<Compile Include="Mvc\IRenderController.cs" />
<Compile Include="Mvc\RenderIndexActionSelectorAttribute.cs" />
<Compile Include="PropertyEditors\DatePreValueEditor.cs" />
<Compile Include="RequestLifespanMessagesFactory.cs" />
<Compile Include="Scheduling\LatchedBackgroundTaskBase.cs" />
<Compile Include="Security\Identity\ExternalSignInAutoLinkOptions.cs" />
<Compile Include="Security\Identity\FixWindowsAuthMiddlware.cs" />
<Compile Include="Security\Identity\ForceRenewalCookieAuthenticationHandler.cs" />
<Compile Include="Security\Identity\ForceRenewalCookieAuthenticationMiddleware.cs" />
<Compile Include="Security\Identity\GetUserSecondsMiddleWare.cs" />
<Compile Include="Media\EmbedProviders\OEmbedJson.cs" />
<Compile Include="Media\EmbedProviders\OEmbedResponse.cs" />
<Compile Include="Media\EmbedProviders\OEmbedPhoto.cs" />
<Compile Include="Security\Identity\IUmbracoBackOfficeTwoFactorOptions.cs" />
<Compile Include="Security\OwinExtensions.cs" />
<Compile Include="Security\WebAuthExtensions.cs" />
<Compile Include="UmbracoDefaultOwinStartup.cs" />
<Compile Include="IUmbracoContextAccessor.cs" />
<Compile Include="Models\ContentEditing\Relation.cs" />
@@ -569,7 +576,6 @@
<Compile Include="Security\Identity\AuthenticationManagerExtensions.cs" />
<Compile Include="Security\Identity\BackOfficeCookieManager.cs" />
<Compile Include="Security\Identity\FormsAuthenticationSecureDataFormat.cs" />
<Compile Include="Security\Identity\OwinExtensions.cs" />
<Compile Include="Security\Identity\UmbracoBackOfficeCookieAuthOptions.cs" />
<Compile Include="Scheduling\TaskAndFactoryExtensions.cs" />
<Compile Include="SingletonUmbracoContextAccessor.cs" />

View File

@@ -149,72 +149,7 @@ namespace Umbraco.Web
#endregion
#region Methods
/// <summary>
/// Determines if we should authenticate the request
/// </summary>
/// <param name="request"></param>
/// <param name="originalRequestUrl"></param>
/// <returns></returns>
/// <remarks>
/// We auth the request when:
/// * it is a back office request
/// * it is an installer request
/// * it is a /base request
/// * it is a preview request
/// </remarks>
internal static bool ShouldAuthenticateRequest(HttpRequestBase request, Uri originalRequestUrl)
{
if (//check back office
request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)
//check installer
|| request.Url.IsInstallerRequest()
//detect in preview
|| (request.HasPreviewCookie() && request.Url != null && request.Url.AbsolutePath.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)) == false)
//check for base
|| BaseRest.BaseRestHandler.IsBaseRestRequest(originalRequestUrl))
{
return true;
}
return false;
}
//private static readonly ConcurrentHashSet<string> IgnoreTicketRenewUrls = new ConcurrentHashSet<string>();
///// <summary>
///// Determines if the authentication ticket should be renewed with a new timeout
///// </summary>
///// <param name="url"></param>
///// <param name="httpContext"></param>
///// <returns></returns>
///// <remarks>
///// We do not want to renew the ticket when we are checking for the user's remaining timeout unless -
///// UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn == true
///// </remarks>
//internal static bool ShouldIgnoreTicketRenew(Uri url, HttpContextBase httpContext)
//{
// //this setting will renew the ticket for all requests.
// if (UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn)
// {
// return false;
// }
// //initialize the ignore ticket urls - we don't need to lock this, it's concurrent and a hashset
// // we don't want to have to gen the url each request so this will speed things up a teeny bit.
// if (IgnoreTicketRenewUrls.Any() == false)
// {
// var urlHelper = new UrlHelper(new RequestContext(httpContext, new RouteData()));
// var checkSessionUrl = urlHelper.GetUmbracoApiService<AuthenticationController>(controller => controller.GetRemainingTimeoutSeconds());
// IgnoreTicketRenewUrls.Add(checkSessionUrl);
// }
// if (IgnoreTicketRenewUrls.Any(x => url.AbsolutePath.StartsWith(x)))
// {
// return true;
// }
// return false;
//}
/// <summary>
/// Checks the current request and ensures that it is routable based on the structure of the request and URI
/// </summary>

View File

@@ -1,23 +1,25 @@
using System.Web.Http.Filters;
using System;
using System.ComponentModel;
using System.Web.Http.Filters;
using Umbraco.Core.Security;
namespace Umbraco.Web.WebApi.Filters
{
/// <summary>
/// A filter that is used to remove the authorization cookie for the current user when the request is successful
/// </summary>
/// <remarks>
/// This is used so that we can log a user OUT in conjunction with using other filters that modify the cookies collection.
/// SD: I beleive this is a bug with web api since if you modify the cookies collection on the HttpContext.Current and then
/// use a filter to write the cookie headers, the filter seems to have no affect at all.
/// </remarks>
[Obsolete("This is no longer used and will be removed from the codebase in the future, use OWIN IAuthenticationManager.SignOut instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class UmbracoBackOfficeLogoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext context)
{
if (context.Response == null) return;
if (context.Response.IsSuccessStatusCode == false) return;
//this clears out all of our cookies
context.Response.UmbracoLogoutWebApi();
//this calls the underlying owin sign out logic - which should call the
// auth providers middleware callbacks if using custom auth middleware
context.Request.TryGetOwinContext().Result.Authentication.SignOut();
}
}
}

View File

@@ -1705,6 +1705,10 @@ namespace umbraco
// propagate the user's context
// zb-00004 #29956 : refactor cookies names & handling
//TODO: This is the worst thing ever. This will also not work if people decide to put their own
// custom auth system in place.
HttpCookie inCookie = StateHelper.Cookies.UserContext.RequestCookie;
var cookie = new Cookie(inCookie.Name, inCookie.Value, inCookie.Path,
HttpContext.Current.Request.ServerVariables["SERVER_NAME"]);

View File

@@ -27,7 +27,9 @@ namespace dashboardUtilities
if (Uri.TryCreate(url, UriKind.Absolute, out requestUri))
{
var feedProxyXml = xmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig));
if (feedProxyXml != null && feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null)
if (feedProxyXml != null
&& feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null
&& requestUri.Port == 80)
{
using (var client = new WebClient())
{

View File

@@ -207,6 +207,9 @@ namespace UmbracoExamine
if (e.Fields.ContainsKey("_searchEmail") == false)
e.Fields.Add("_searchEmail", e.Node.Attribute("email").Value.Replace(".", " ").Replace("@", " "));
}
if (e.Fields.ContainsKey(IconFieldName) == false)
e.Fields.Add(IconFieldName, (string)e.Node.Attribute("icon"));
}
private static XElement GetMemberItem(int nodeId)

View File

@@ -3,6 +3,7 @@ using System.Reflection;
using System.Web;
using System.Web.UI;
using Umbraco.Core;
using Umbraco.Core.Configuration;
namespace umbraco.BusinessLogic
{
@@ -350,7 +351,7 @@ namespace umbraco.BusinessLogic
* that actually make sense? shouldn't some cookie have _no_ expires?
*/
static readonly Cookie _preview = new Cookie(Constants.Web.PreviewCookieName, TimeSpan.Zero); // was "PreviewSet"
static readonly Cookie _userContext = new Cookie(Constants.Web.AuthCookieName, 30d); // was "UserContext"
static readonly Cookie _userContext = new Cookie(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, 30d); // was "UserContext"
static readonly Cookie _member = new Cookie("UMB_MEMBER", 30d); // was "umbracoMember"
public static Cookie Preview { get { return _preview; } }