Compare commits

..

20 Commits

Author SHA1 Message Date
Kenn Jacobsen 9865596dcc V7: Change the priority of the tree node annotations for contai… (#6083)
(cherry picked from commit 7393d9c372)
2019-08-09 16:47:14 +02:00
Kenn Jacobsen dc44a7a3a7 Fix the disabled upload button in media picker
(cherry picked from commit 79c4d7797c)
2019-08-09 16:41:13 +02:00
Peter Keating ff595df7ca Fix notifications not sending when user has multiple of same type
When a user has multiple notifications of the same type, the user may
not receive the notification depending on the order which the
notification was setup. To fix this the notification logic has changed
from looping over users to looping over notifications.

Fixes #4713.

(cherry picked from commit 01a895bbf7)
2019-08-08 19:49:59 +02:00
Shannon Deminick 686f6752f3 Merge pull request #5951 from umbraco/v7/hotfix/5921-preview-failing
https://github.com/umbraco/Umbraco-CMS/issues/5921 - AB#1794 - Remove…
2019-07-23 16:58:13 +10:00
Shannon Deminick 23ccdeb2cf Merge pull request #5868 from umbraco/v7/hotfix/5835-AB#1693-add-null-checks-when-using-dataTypeId
Added null checks when *.dataTypeId is used
2019-07-22 13:44:05 +10:00
Shannon 6148ce4e52 oops no es6 support in 7.x 2019-07-22 13:41:26 +10:00
Shannon 06850ed25c Merge branch 'v7/7.15' into v7/hotfix/5835-AB#1693-add-null-checks-when-using-dataTypeId 2019-07-22 13:40:35 +10:00
Shannon 0320a56cb5 Fixes up dataTypeId var declarations 2019-07-22 13:27:51 +10:00
Bjarke Berg cd27bb210f https://github.com/umbraco/Umbraco-CMS/issues/5921 - AB#1794 - Remove trashed nodes from sql when building xml for previews 2019-07-19 11:25:33 +02:00
Bjarke Berg 67f680a675 Revert "https://github.com/umbraco/Umbraco-CMS/issues/5921 - AB#1794 - Remove trashed nodes from sql when building xml for previews"
This reverts commit 47c3e3a7
2019-07-19 11:24:01 +02:00
Bjarke Berg 47c3e3a79f https://github.com/umbraco/Umbraco-CMS/issues/5921 - AB#1794 - Remove trashed nodes from sql when building xml for previews 2019-07-19 11:20:31 +02:00
Shannon f3bfc1ffb2 Puts back UmbracoAntiForgeryAdditionalDataProvider for backwards compat reasons but it is not used 2019-07-18 16:18:11 +10:00
Shannon 89cd655df2 Merge remote-tracking branch 'origin/v7/bugfix/build-without-vs2017' into v7/7.15 2019-07-17 21:48:21 +10:00
Shannon 14c4c4815d bumps version 2019-07-17 21:35:43 +10:00
Shannon bfb69a34ef re-adds back in the serialization overloads for the custom exception, re-adds detailed error messages, adds more documentation. Adds unit tests. 2019-07-17 21:15:18 +10:00
Shannon d52420183e Cherry picks ValidateUmbracoFormRouteStringAttribute implementation and fixes up some logic 2019-07-16 23:03:26 +10:00
skttl 2aaca865e7 changes innerjoin to left join, to allow folders in sql query 2019-07-16 07:26:26 +02:00
Bjarke Berg 59bf24c461 Added null checks when *.dataTypeId is used 2019-07-12 11:22:31 +02:00
Bjarke Berg 2748c3635d Merge pull request #5863 from umbraco/v7/bugfix/5822-entityservice-overloads
Fixes Change to Entity Service GetByKey isn't breaking in 7.15 #5822
2019-07-12 09:29:45 +02:00
Shannon 2252db0d55 Fixes 5822 by reinstating optional method overloads of loadBaseType 2019-07-12 13:40:21 +10:00
38 changed files with 1796 additions and 1667 deletions
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.15.0")]
[assembly: AssemblyInformationalVersion("7.15.0")]
[assembly: AssemblyFileVersion("7.15.1")]
[assembly: AssemblyInformationalVersion("7.15.1")]
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.15.0");
private static readonly Version Version = new Version("7.15.1");
/// <summary>
/// Gets the current version of Umbraco.
File diff suppressed because it is too large Load Diff
@@ -370,7 +370,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (isMedia)
{
entitySql.InnerJoin("cmsMedia media").On("media.nodeId = umbracoNode.id");
entitySql.LeftJoin("cmsMedia media").On("media.nodeId = umbracoNode.id");
}
entitySql.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType");
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using Umbraco.Core.CodeAnnotations;
@@ -717,5 +718,32 @@ namespace Umbraco.Core.Services
}
return node.NodeId;
}
#region Obsolete - only here for compat
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true) => GetByKey(key);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity Get(int id, bool loadBaseType = true) => Get(id);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity GetByKey(Guid key, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true) => GetByKey(key, umbracoObjectType);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity GetByKey<T>(Guid key, bool loadBaseType = true) where T : IUmbracoEntity => GetByKey<T>(key);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true) => Get(id, umbracoObjectType);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity Get<T>(int id, bool loadBaseType = true) where T : IUmbracoEntity => Get<T>(id);
#endregion
}
}
+25 -1
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -55,6 +56,10 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
IUmbracoEntity GetByKey(Guid key);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true);
/// <summary>
/// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph.
/// </summary>
@@ -62,9 +67,13 @@ namespace Umbraco.Core.Services
/// By default this will load the base type <see cref="IUmbracoEntity"/> with a minimum set of properties.
/// </returns>
/// <param name="id">Id of the object to retrieve</param>
/// <returns>An <see cref="IUmbracoEntity"/></returns>
/// <returns>An <see cref="IUmbracoEntity"/></returns>
IUmbracoEntity Get(int id);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity Get(int id, bool loadBaseType = true);
/// <summary>
/// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph.
/// </summary>
@@ -76,6 +85,14 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
IUmbracoEntity GetByKey(Guid key, UmbracoObjectTypes umbracoObjectType);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity GetByKey(Guid key, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity GetByKey<T>(Guid key, bool loadBaseType = true) where T : IUmbracoEntity;
/// <summary>
/// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph.
/// </summary>
@@ -87,6 +104,9 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true);
/// <summary>
/// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph.
@@ -99,6 +119,10 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
IUmbracoEntity Get<T>(int id) where T : IUmbracoEntity;
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity Get<T>(int id, bool loadBaseType = true) where T : IUmbracoEntity;
/// <summary>
/// Gets the parent of entity by its id
/// </summary>
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -145,6 +146,10 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
IUmbracoEntity GetChildEntityFromRelation(IRelation relation);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity GetChildEntityFromRelation(IRelation relation, bool loadBaseType = false);
/// <summary>
/// Gets the Parent object from a Relation as an <see cref="IUmbracoEntity"/>
/// </summary>
@@ -152,6 +157,10 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
IUmbracoEntity GetParentEntityFromRelation(IRelation relation);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUmbracoEntity GetParentEntityFromRelation(IRelation relation, bool loadBaseType = false);
/// <summary>
/// Gets the Parent and Child objects from a Relation as a <see cref="Tuple"/>"/> with <see cref="IUmbracoEntity"/>.
/// </summary>
@@ -159,6 +168,10 @@ namespace Umbraco.Core.Services
/// <returns>Returns a Tuple with Parent (item1) and Child (item2)</returns>
Tuple<IUmbracoEntity, IUmbracoEntity> GetEntitiesFromRelation(IRelation relation);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
Tuple<IUmbracoEntity, IUmbracoEntity> GetEntitiesFromRelation(IRelation relation, bool loadBaseType = false);
/// <summary>
/// Gets the Child objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
/// </summary>
@@ -166,6 +179,10 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
IEnumerable<IUmbracoEntity> GetChildEntitiesFromRelations(IEnumerable<IRelation> relations);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<IUmbracoEntity> GetChildEntitiesFromRelations(IEnumerable<IRelation> relations, bool loadBaseType = false);
/// <summary>
/// Gets the Parent objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
/// </summary>
@@ -173,6 +190,10 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
IEnumerable<IUmbracoEntity> GetParentEntitiesFromRelations(IEnumerable<IRelation> relations);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<IUmbracoEntity> GetParentEntitiesFromRelations(IEnumerable<IRelation> relations, bool loadBaseType = false);
/// <summary>
/// Gets the Parent and Child objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
/// </summary>
@@ -181,6 +202,12 @@ namespace Umbraco.Core.Services
IEnumerable<Tuple<IUmbracoEntity, IUmbracoEntity>> GetEntitiesFromRelations(
IEnumerable<IRelation> relations);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<Tuple<IUmbracoEntity, IUmbracoEntity>> GetEntitiesFromRelations(
IEnumerable<IRelation> relations,
bool loadBaseType = false);
/// <summary>
/// Relates two objects by their entity Ids.
/// </summary>
@@ -124,40 +124,52 @@ namespace Umbraco.Core.Services
var notifications = GetUsersNotifications(users.Select(x => x.Id), action, Enumerable.Empty<int>(), Constants.ObjectTypes.DocumentGuid).ToList();
if (notifications.Count == 0) break;
var i = 0;
foreach (var user in users)
{
// continue if there's no notification for this user
if (notifications[i].UserId != user.Id) continue; // next user
for (var j = 0; j < entitiesL.Count; j++)
{
var content = entitiesL[j];
var path = paths[j];
// test if the notification applies to the path ie to this entity
if (path.Contains(notifications[i].EntityId) == false) continue; // next entity
if (prevVersionDictionary.ContainsKey(content.Id) == false)
{
prevVersionDictionary[content.Id] = GetPreviousVersion(content.Id);
}
// queue notification
var req = CreateNotificationRequest(operatingUser, user, content, prevVersionDictionary[content.Id], actionName, http, createSubject, createBody);
Enqueue(req);
}
// skip other notifications for this user, essentially this means moving i to the next index of notifications
// for the next user.
do
{
i++;
} while (i < notifications.Count && notifications[i].UserId == user.Id);
if (i >= notifications.Count) break; // break if no more notifications
}
while (notifications.Count > 0)
{
var notification = notifications[0];
var isMatched = false;
// grab user whose associated to the notification
var user = users.Where(x => x.Id == notification.UserId).FirstOrDefault();
if (user == null)
{
notifications.RemoveAll(x => x.UserId == notification.UserId);
}
for (var j = 0; j < entitiesL.Count; j++)
{
var content = entitiesL[j];
var path = paths[j];
// test if the notification applies to the path ie to this entity
if (path.Contains(notification.EntityId) == false) continue; // next entity
isMatched = true;
if (prevVersionDictionary.ContainsKey(content.Id) == false)
{
prevVersionDictionary[content.Id] = GetPreviousVersion(content.Id);
}
// queue notification
var req = CreateNotificationRequest(operatingUser, user, content, prevVersionDictionary[content.Id], actionName, http, createSubject, createBody);
Enqueue(req);
// don't process any further entities as a notification has been sent
break;
}
// when a match has been found, skip other notifications for user.
if (isMatched)
{
notifications.RemoveAll(x => x.UserId == notification.UserId);
continue;
}
notifications.Remove(notification);
}
// load more users if any
id = users.Count == pagesz ? users.Last().Id + 1 : -1;
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
@@ -736,5 +737,31 @@ namespace Umbraco.Core.Services
/// </summary>
public static event TypedEventHandler<IRelationService, SaveEventArgs<IRelationType>> SavedRelationType;
#endregion
#region Obsolete - only here for compat
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity GetChildEntityFromRelation(IRelation relation, bool loadBaseType = false) => GetChildEntityFromRelation(relation);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IUmbracoEntity GetParentEntityFromRelation(IRelation relation, bool loadBaseType = false) => GetParentEntityFromRelation(relation);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public Tuple<IUmbracoEntity, IUmbracoEntity> GetEntitiesFromRelation(IRelation relation, bool loadBaseType = false) => GetEntitiesFromRelation(relation);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<IUmbracoEntity> GetChildEntitiesFromRelations(IEnumerable<IRelation> relations, bool loadBaseType = false) => GetChildEntitiesFromRelations(relations);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<IUmbracoEntity> GetParentEntitiesFromRelations(IEnumerable<IRelation> relations, bool loadBaseType = false) => GetParentEntitiesFromRelations(relations);
[Obsolete("Use the overload that doesn't specify loadBaseType instead, loadBaseType will not affect any results")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<Tuple<IUmbracoEntity, IUmbracoEntity>> GetEntitiesFromRelations(IEnumerable<IRelation> relations, bool loadBaseType = false) => GetEntitiesFromRelations(relations);
#endregion
}
}
@@ -1,157 +0,0 @@
using System.Collections.Specialized;
using System.Web;
using System.Web.Helpers;
using Moq;
using Newtonsoft.Json;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web.Mvc;
using Umbraco.Web.Security;
namespace Umbraco.Tests.Security
{
[TestFixture]
public class UmbracoAntiForgeryAdditionalDataProviderTests
{
[Test]
public void Test_Wrapped_Non_BeginUmbracoForm()
{
var wrapped = Mock.Of<IAntiForgeryAdditionalDataProvider>(x => x.GetAdditionalData(It.IsAny<HttpContextBase>()) == "custom");
var provider = new UmbracoAntiForgeryAdditionalDataProvider(wrapped);
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
var data = provider.GetAdditionalData(httpContextFactory.HttpContext);
Assert.IsTrue(data.DetectIsJson());
var json = JsonConvert.DeserializeObject<UmbracoAntiForgeryAdditionalDataProvider.AdditionalData>(data);
Assert.AreEqual(null, json.Ufprt);
Assert.IsTrue(json.Stamp != default);
Assert.AreEqual("custom", json.WrappedValue);
}
[Test]
public void Null_Wrapped_Non_BeginUmbracoForm()
{
var provider = new UmbracoAntiForgeryAdditionalDataProvider(null);
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
var data = provider.GetAdditionalData(httpContextFactory.HttpContext);
Assert.IsTrue(data.DetectIsJson());
var json = JsonConvert.DeserializeObject<UmbracoAntiForgeryAdditionalDataProvider.AdditionalData>(data);
Assert.AreEqual(null, json.Ufprt);
Assert.IsTrue(json.Stamp != default);
Assert.AreEqual("default", json.WrappedValue);
}
[Test]
public void Validate_Non_Json()
{
var provider = new UmbracoAntiForgeryAdditionalDataProvider(null);
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "hello");
Assert.IsFalse(isValid);
}
[Test]
public void Validate_Invalid_Json()
{
var provider = new UmbracoAntiForgeryAdditionalDataProvider(null);
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '0'}");
Assert.IsFalse(isValid);
isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': ''}");
Assert.IsFalse(isValid);
isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'hello': 'world'}");
Assert.IsFalse(isValid);
}
[Test]
public void Validate_No_Request_Ufprt()
{
var provider = new UmbracoAntiForgeryAdditionalDataProvider(null);
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
//there is a ufprt in the additional data, but not in the request
var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': 'ASBVDFDFDFDF'}");
Assert.IsFalse(isValid);
}
[Test]
public void Validate_No_AdditionalData_Ufprt()
{
var provider = new UmbracoAntiForgeryAdditionalDataProvider(null);
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
var requestMock = Mock.Get(httpContextFactory.HttpContext.Request);
requestMock.SetupGet(x => x["ufprt"]).Returns("ABCDEFG");
//there is a ufprt in the additional data, but not in the request
var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': ''}");
Assert.IsFalse(isValid);
}
[Test]
public void Validate_No_AdditionalData_Or_Request_Ufprt()
{
var provider = new UmbracoAntiForgeryAdditionalDataProvider(null);
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
//there is a ufprt in the additional data, but not in the request
var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': ''}");
Assert.IsTrue(isValid);
}
[Test]
public void Validate_Request_And_AdditionalData_Ufprt()
{
var provider = new UmbracoAntiForgeryAdditionalDataProvider(null);
var routeParams1 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco";
var routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco";
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
var requestMock = Mock.Get(httpContextFactory.HttpContext.Request);
requestMock.SetupGet(x => x["ufprt"]).Returns(routeParams1.EncryptWithMachineKey());
var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}");
Assert.IsTrue(isValid);
routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Invalid")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco";
isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}");
Assert.IsFalse(isValid);
}
[Test]
public void Validate_Wrapped_Request_And_AdditionalData_Ufprt()
{
var wrapped = Mock.Of<IAntiForgeryAdditionalDataProvider>(x => x.ValidateAdditionalData(It.IsAny<HttpContextBase>(), "custom") == true);
var provider = new UmbracoAntiForgeryAdditionalDataProvider(wrapped);
var routeParams1 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco";
var routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco";
var httpContextFactory = new FakeHttpContextFactory("/hello/world");
var requestMock = Mock.Get(httpContextFactory.HttpContext.Request);
requestMock.SetupGet(x => x["ufprt"]).Returns(routeParams1.EncryptWithMachineKey());
var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}");
Assert.IsFalse(isValid);
isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'custom', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}");
Assert.IsTrue(isValid);
routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Invalid")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco";
isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}");
Assert.IsFalse(isValid);
}
}
}
+1 -1
View File
@@ -197,7 +197,6 @@
<Compile Include="Packaging\PackageExtractionTests.cs" />
<Compile Include="Persistence\Repositories\SimilarNodeNameTests.cs" />
<Compile Include="PublishedContent\StronglyTypedModels\Home.cs" />
<Compile Include="Security\UmbracoAntiForgeryAdditionalDataProviderTests.cs" />
<Compile Include="Services\AuditServiceTests.cs" />
<Compile Include="Services\ConsentServiceTests.cs" />
<Compile Include="Services\MemberGroupServiceTests.cs" />
@@ -399,6 +398,7 @@
<Compile Include="Models\ContentExtensionsTests.cs" />
<Compile Include="Models\UserExtensionsTests.cs" />
<Compile Include="Web\Mvc\MergeParentContextViewDataAttributeTests.cs" />
<Compile Include="Web\Mvc\ValidateUmbracoFormRouteStringAttributeTests.cs" />
<Compile Include="Web\Mvc\ViewDataDictionaryExtensionTests.cs" />
<Compile Include="Persistence\PetaPocoExtensionsTest.cs" />
<Compile Include="Persistence\Querying\ContentTypeSqlMappingTests.cs" />
@@ -4,7 +4,8 @@ using Umbraco.Web;
namespace Umbraco.Tests.Web.Mvc
{
[TestFixture]
[TestFixture]
public class HtmlHelperExtensionMethodsTests
{
[SetUp]
@@ -0,0 +1,37 @@
using NUnit.Framework;
using Umbraco.Web;
using Umbraco.Web.Mvc;
namespace Umbraco.Tests.Web.Mvc
{
[TestFixture]
public class ValidateUmbracoFormRouteStringAttributeTests
{
[Test]
public void Validate_Route_String()
{
var attribute = new ValidateUmbracoFormRouteStringAttribute();
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(null, null, null, null));
const string ControllerName = "Test";
const string ControllerAction = "Index";
const string Area = "MyArea";
var validUfprt = UmbracoHelper.CreateEncryptedRouteString(ControllerName, ControllerAction, Area);
var invalidUfprt = validUfprt + "z";
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(invalidUfprt, null, null, null));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, "doesntMatch"));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, null));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, "doesntMatch", Area));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, null, Area));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, "doesntMatch", ControllerAction, Area));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, null, ControllerAction, Area));
Assert.DoesNotThrow(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, Area));
Assert.DoesNotThrow(() => attribute.ValidateRouteString(validUfprt, ControllerName.ToLowerInvariant(), ControllerAction.ToLowerInvariant(), Area.ToLowerInvariant()));
}
}
}
@@ -409,7 +409,8 @@ function entityResource($q, $http, umbRequestHelper) {
pageNumber: 100,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder"
orderBy: "SortOrder",
dataTypeId: null
};
if (options === undefined) {
options = {};
@@ -426,6 +427,7 @@ function entityResource($q, $http, umbRequestHelper) {
options.orderDirection = "Descending";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
+23 -21
View File
@@ -327,27 +327,6 @@ li.root > div > a.umb-options {
div.not-published > i.icon,div.not-published > a{
opacity: 0.6;
}
div.protected:before{
content:"\e256";
font-family: 'icomoon';
color: @red;
position: absolute;
font-size: 20px;
padding-left: 7px;
padding-top: 7px;
bottom: 0;
}
div.has-unpublished-version:before{
content:"\e25a";
font-family: 'icomoon';
color: @green;
position: absolute;
font-size: 20px;
padding-left: 7px;
padding-top: 7px;
bottom: 0;
}
div.not-allowed > i.icon,div.not-allowed > a{
cursor: not-allowed;
@@ -380,6 +359,29 @@ div.locked:before{
bottom: 0;
}
div.has-unpublished-version:before {
content: "\e25a";
font-family: 'icomoon';
color: @green;
position: absolute;
font-size: 20px;
padding-left: 7px;
padding-top: 7px;
bottom: 0;
}
div.protected:before {
content: "\e256";
font-family: 'icomoon';
color: @red;
position: absolute;
font-size: 20px;
padding-left: 7px;
padding-top: 7px;
bottom: 0;
}
.umb-tree li div.no-access .umb-tree-icon,
.umb-tree li div.no-access .root-link,
.umb-tree li div.no-access .umb-tree-item__label {
@@ -14,7 +14,7 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController",
searchFromId: null,
searchFromName: null,
showSearch: false,
dataTypeId: $scope.model.dataTypeId,
dataTypeId: ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null,
results: [],
selectedSearchResults: []
}
@@ -12,17 +12,22 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
$scope.model.title = localizationService.localize("defaultdialogs_selectLink");
}
var dataTypeId = null;
if(dialogOptions && dialogOptions.dataTypeId){
dataTypeId = dialogOptions.dataTypeId;
}
$scope.dialogTreeEventHandler = $({});
$scope.model.target = {};
$scope.searchInfo = {
searchFromId: null,
searchFromName: null,
showSearch: false,
dataTypeId: dialogOptions.dataTypeId,
dataTypeId: dataTypeId,
results: [],
selectedSearchResults: []
};
$scope.customTreeParams = dialogOptions.dataTypeId ? "dataTypeId=" + dialogOptions.dataTypeId : "";
$scope.customTreeParams = dataTypeId !== null ? "dataTypeId=" + dataTypeId : "";
$scope.showTarget = $scope.model.hideTarget !== true;
if (dialogOptions.currentTarget) {
@@ -115,12 +120,13 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
startNodeId = -1;
startNodeIsVirtual = true;
}
$scope.mediaPickerOverlay = {
view: "mediapicker",
startNodeId: startNodeId,
startNodeIsVirtual: startNodeIsVirtual,
show: true,
dataTypeId: dialogOptions.dataTypeId,
dataTypeId: dataTypeId,
submit: function (model) {
var media = model.selectedImages[0];
@@ -44,13 +44,17 @@ angular.module("umbraco")
$scope.acceptedMediatypes = types;
});
var dataTypeId = null;
if($scope.model && $scope.model.dataTypeId) {
dataTypeId = $scope.model.dataTypeId;
}
$scope.searchOptions = {
pageNumber: 1,
pageSize: 100,
totalItems: 0,
totalPages: 0,
filter: '',
dataTypeId: $scope.model.dataTypeId
dataTypeId: dataTypeId
};
//preload selected item
@@ -157,7 +161,8 @@ angular.module("umbraco")
}
if (folder.id > 0) {
entityResource.getAncestors(folder.id, "media", { dataTypeId: $scope.model.dataTypeId })
entityResource.getAncestors(folder.id, "media", { dataTypeId: dataTypeId })
.then(function (anc) {
$scope.path = _.filter(anc,
function (f) {
@@ -262,7 +267,7 @@ angular.module("umbraco")
// also make sure the node is not trashed
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1 && node.trashed === false) {
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder", path: node.path });
return true;
} else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
@@ -309,6 +314,7 @@ angular.module("umbraco")
if ($scope.searchOptions.filter) {
searchMedia();
} else {
// reset pagination
$scope.searchOptions = {
pageNumber: 1,
@@ -316,7 +322,7 @@ angular.module("umbraco")
totalItems: 0,
totalPages: 0,
filter: '',
dataTypeId: $scope.model.dataTypeId
dataTypeId: dataTypeId
};
getChildren($scope.currentFolder.id);
}
@@ -4,6 +4,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
var tree = null;
var dialogOptions = $scope.model;
$scope.treeReady = false;
$scope.dialogTreeEventHandler = $({});
$scope.section = dialogOptions.section;
@@ -16,7 +17,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
searchFromId: dialogOptions.startNodeId,
searchFromName: null,
showSearch: false,
dataTypeId: dialogOptions.dataTypeId,
dataTypeId: (dialogOptions && dialogOptions.dataTypeId) ? dialogOptions.dataTypeId : null,
results: [],
selectedSearchResults: []
}
@@ -68,11 +68,11 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
showPathOnHover: false,
dataTypeId: null,
maxNumber: 1,
minNumber : 0,
minNumber: 0,
startNode: {
query: "",
type: "content",
id: $scope.model.config.startNodeId ? $scope.model.config.startNodeId : -1 // get start node for simple Content Picker
id: $scope.model.config.startNodeId ? $scope.model.config.startNodeId : -1 // get start node for simple Content Picker
}
};
@@ -104,8 +104,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
var entityType = $scope.model.config.startNode.type === "member"
? "Member"
: $scope.model.config.startNode.type === "media"
? "Media"
: "Document";
? "Media"
: "Document";
$scope.allowOpenButton = entityType === "Document";
$scope.allowEditButton = entityType === "Document";
$scope.allowRemoveButton = true;
@@ -144,7 +144,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
dialogOptions.filterCssClass = "not-allowed";
var currFilter = dialogOptions.filter;
//now change the filter to be a method
dialogOptions.filter = function(i) {
dialogOptions.filter = function (i) {
//filter out the list view nodes
if (i.metaData.isContainer) {
return true;
@@ -179,29 +179,30 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
}
//dialog
$scope.openContentPicker = function() {
$scope.contentPickerOverlay = dialogOptions;
$scope.contentPickerOverlay.view = "treepicker";
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.dataTypeId = $scope.model.dataTypeId;
$scope.openContentPicker = function () {
$scope.contentPickerOverlay = dialogOptions;
$scope.contentPickerOverlay.view = "treepicker";
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.dataTypeId = ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null;
$scope.contentPickerOverlay.submit = function(model) {
$scope.contentPickerOverlay.submit = function (model) {
if (angular.isArray(model.selection)) {
_.each(model.selection, function (item, i) {
$scope.add(item);
});
angularHelper.getCurrentForm($scope).$setDirty();
}
if (angular.isArray(model.selection)) {
_.each(model.selection, function (item, i) {
$scope.add(item);
});
angularHelper.getCurrentForm($scope).$setDirty();
}
$scope.contentPickerOverlay.show = false;
$scope.contentPickerOverlay = null;
}
$scope.contentPickerOverlay.show = false;
$scope.contentPickerOverlay = null;
}
$scope.contentPickerOverlay.close = function(oldModel) {
$scope.contentPickerOverlay.show = false;
$scope.contentPickerOverlay = null;
}
$scope.contentPickerOverlay.close = function (oldModel) {
$scope.contentPickerOverlay.show = false;
$scope.contentPickerOverlay = null;
}
};
@@ -241,13 +242,13 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
$scope.renderModel = [];
};
$scope.openMiniEditor = function(node) {
miniEditorHelper.launchMiniEditor(node).then(function(updatedNode){
$scope.openMiniEditor = function (node) {
miniEditorHelper.launchMiniEditor(node).then(function (updatedNode) {
// update the node
node.name = updatedNode.name;
node.published = updatedNode.hasPublishedVersion;
if(entityType !== "Member") {
entityResource.getUrl(updatedNode.id, entityType).then(function(data){
if (entityType !== "Member") {
entityResource.getUrl(updatedNode.id, entityType).then(function (data) {
node.url = data;
});
}
@@ -256,7 +257,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
//when the scope is destroyed we need to unsubscribe
$scope.$on('$destroy', function () {
if(unsubscribe) {
if (unsubscribe) {
unsubscribe();
}
});
@@ -265,12 +266,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
//load current data if anything selected
if (modelIds.length > 0) {
entityResource.getByIds(modelIds, entityType).then(function(data) {
entityResource.getByIds(modelIds, entityType).then(function (data) {
_.each(modelIds,
function(id, i) {
function (id, i) {
var entity = _.find(data,
function(d) {
function (d) {
return $scope.model.config.idType === "udi" ? (d.udi == id) : (d.id == id);
});
@@ -294,10 +295,10 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
function setEntityUrl(entity) {
// get url for content and media items
if(entityType !== "Member") {
entityResource.getUrl(entity.id, entityType).then(function(data){
if (entityType !== "Member") {
entityResource.getUrl(entity.id, entityType).then(function (data) {
// update url
angular.forEach($scope.renderModel, function(item){
angular.forEach($scope.renderModel, function (item) {
if (item.id === entity.id) {
if (entity.trashed) {
item.url = localizationService.dictionary.general_recycleBin;
@@ -319,7 +320,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
function addSelectedItem(item) {
// set icon
if(item.icon) {
if (item.icon) {
item.icon = iconHelper.convertFromLegacyIcon(item.icon);
}
@@ -354,7 +355,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
function setSortingState(items) {
// disable sorting if the list only consist of one item
if(items.length > 1) {
if (items.length > 1) {
$scope.sortableOptions.disabled = false;
} else {
$scope.sortableOptions.disabled = true;
@@ -16,12 +16,13 @@ angular.module("umbraco")
}
$scope.setImage = function(){
$scope.mediaPickerOverlay = {};
$scope.mediaPickerOverlay.view = "mediapicker";
$scope.mediaPickerOverlay.startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined;
$scope.mediaPickerOverlay.startNodeIsVirtual = $scope.mediaPickerOverlay.startNodeId ? $scope.model.config.startNodeIsVirtual : undefined;
$scope.mediaPickerOverlay.dataTypeId = $scope.model.dataTypeId;
$scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined;
$scope.mediaPickerOverlay.startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : null;
$scope.mediaPickerOverlay.startNodeIsVirtual = $scope.mediaPickerOverlay.startNodeId ? $scope.model.config.startNodeIsVirtual : null;
$scope.mediaPickerOverlay.dataTypeId = ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null;
$scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : null;
$scope.mediaPickerOverlay.showDetails = true;
$scope.mediaPickerOverlay.disableFolderSelect = true;
$scope.mediaPickerOverlay.onlyImages = true;
@@ -10,14 +10,17 @@
vm.openMacroPicker = openMacroPicker;
vm.openEmbed = openEmbed;
var dataTypeId = ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null;
function openLinkPicker(editor, currentTarget, anchorElement) {
entityResource.getAnchors(JSON.stringify($scope.model.value)).then(function(anchorValues) {
vm.linkPickerOverlay = {
view: "linkpicker",
currentTarget: currentTarget,
anchors: anchorValues,
dataTypeId: $scope.model.dataTypeId,
dataTypeId: dataTypeId,
ignoreUserStartNodes : $scope.model.config.ignoreUserStartNodes,
show: true,
submit: function(model) {
@@ -46,7 +49,7 @@
showDetails: true,
startNodeId: startNodeId,
startNodeIsVirtual: startNodeIsVirtual,
dataTypeId: $scope.model.dataTypeId,
dataTypeId: dataTypeId,
view: "mediapicker",
show: true,
submit: function(model) {
@@ -107,13 +107,13 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
};
$scope.add = function() {
$scope.mediaPickerOverlay = {
view: "mediapicker",
title: "Select media",
startNodeId: $scope.model.config.startNodeId,
startNodeIsVirtual: $scope.model.config.startNodeIsVirtual,
dataTypeId: $scope.model.dataTypeId,
dataTypeId: ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null,
multiPicker: multiPicker,
onlyImages: onlyImages,
disableFolderSelect: disableFolderSelect,
@@ -71,7 +71,7 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en
$scope.linkPickerOverlay = {
view: "linkpicker",
currentTarget: target,
dataTypeId: $scope.model.dataTypeId,
dataTypeId: ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null,
ignoreUserStartNodes : $scope.model.config.ignoreUserStartNodes,
show: true,
submit: function (model) {
@@ -18,14 +18,17 @@
$scope.currentEditLink = null;
$scope.hasError = false;
var dataTypeId = ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null;
$scope.internal = function($event) {
$scope.currentEditLink = null;
$scope.contentPickerOverlay = {};
$scope.contentPickerOverlay.view = "contentpicker";
$scope.contentPickerOverlay.multiPicker = false;
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.dataTypeId = $scope.model.dataTypeId;
$scope.contentPickerOverlay.dataTypeId = dataTypeId;
$scope.contentPickerOverlay.idType = $scope.model.config.idType ? $scope.model.config.idType : "int";
$scope.contentPickerOverlay.submit = function(model) {
@@ -45,13 +48,14 @@
};
$scope.selectInternal = function ($event, link) {
$scope.currentEditLink = link;
$scope.contentPickerOverlay = {};
$scope.contentPickerOverlay.view = "contentpicker";
$scope.contentPickerOverlay.multiPicker = false;
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.dataTypeId = $scope.model.dataTypeId;
$scope.contentPickerOverlay.dataTypeId = dataTypeId;
$scope.contentPickerOverlay.idType = $scope.model.config.idType ? $scope.model.config.idType : "int";
$scope.contentPickerOverlay.submit = function(model) {
@@ -52,6 +52,8 @@ angular.module("umbraco")
editorConfig.maxImageSize = tinyMceService.defaultPrevalues().maxImageSize;
}
var dataTypeId = ($scope.model && $scope.model.dataTypeId) ? $scope.model.dataTypeId : null;
//queue file loading
if (typeof tinymce === "undefined") { // Don't reload tinymce if already loaded
await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", $scope));
@@ -272,11 +274,12 @@ angular.module("umbraco")
tinyMceService.createLinkPicker(editor, $scope, function(currentTarget, anchorElement) {
entityResource.getAnchors($scope.model.value).then(function(anchorValues){
$scope.linkPickerOverlay = {
view: "linkpicker",
currentTarget: currentTarget,
anchors: anchorValues,
dataTypeId: $scope.model.dataTypeId,
dataTypeId: dataTypeId,
ignoreUserStartNodes: $scope.model.config.ignoreUserStartNodes,
show: true,
submit: function(model) {
@@ -300,7 +303,7 @@ angular.module("umbraco")
startNodeId = -1;
startNodeIsVirtual = true;
}
$scope.mediaPickerOverlay = {
currentTarget: currentTarget,
onlyImages: true,
@@ -308,7 +311,7 @@ angular.module("umbraco")
disableFolderSelect: true,
startNodeId: startNodeId,
startNodeIsVirtual: startNodeIsVirtual,
dataTypeId: $scope.model.dataTypeId,
dataTypeId: dataTypeId,
view: "mediapicker",
show: true,
submit: function(model) {
+2 -2
View File
@@ -1028,9 +1028,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7150</DevelopmentServerPort>
<DevelopmentServerPort>7151</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7150</IISUrl>
<IISUrl>http://localhost:7151</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -12,6 +12,7 @@ namespace Umbraco.Web.Controllers
{
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public ActionResult HandleLogin([Bind(Prefix = "loginModel")]LoginModel model)
{
if (ModelState.IsValid == false)
@@ -13,6 +13,7 @@ namespace Umbraco.Web.Controllers
{
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public ActionResult HandleLogout([Bind(Prefix = "logoutModel")]PostRedirectModel model)
{
if (ModelState.IsValid == false)
@@ -16,6 +16,7 @@ namespace Umbraco.Web.Controllers
{
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public ActionResult HandleUpdateProfile([Bind(Prefix = "profileModel")] ProfileModel model)
{
var provider = global::Umbraco.Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
@@ -11,6 +11,7 @@ namespace Umbraco.Web.Controllers
{
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public ActionResult HandleRegisterMember([Bind(Prefix = "registerModel")]RegisterModel model)
{
if (ModelState.IsValid == false)
@@ -293,13 +293,6 @@ namespace Umbraco.Web
_controllerName = controllerName;
_encryptedString = UmbracoHelper.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals);
//For UmbracoForm's we want to add our routing string to the httpcontext items in the case where anti-forgery tokens are used.
//In which case our custom UmbracoAntiForgeryAdditionalDataProvider will kick in and validate the values in the request against
//the values that will be appended to the token. This essentially means that when anti-forgery tokens are used with UmbracoForm's forms,
//that each token is unique to the controller/action/area instead of the default ASP.Net implementation which is that the token is unique
//per user.
_viewContext.HttpContext.Items["ufprt"] = _encryptedString;
}
@@ -0,0 +1,41 @@
using System;
using System.Net;
using System.Runtime.Serialization;
using System.Web;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Exception that occurs when an Umbraco form route string is invalid
/// </summary>
/// <seealso cref="System.Web.HttpException" />
[Serializable]
public sealed class HttpUmbracoFormRouteStringException : HttpException
{
/// Initializes a new instance of the <see cref="HttpUmbracoFormRouteStringException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that holds the contextual information about the source or destination.</param>
private HttpUmbracoFormRouteStringException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="HttpUmbracoFormRouteStringException" /> class.
/// </summary>
/// <param name="message">The error message displayed to the client when the exception is thrown.</param>
public HttpUmbracoFormRouteStringException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="HttpUmbracoFormRouteStringException" /> class.
/// </summary>
/// <param name="message">The error message displayed to the client when the exception is thrown.</param>
/// <param name="innerException">The <see cref="P:System.Exception.InnerException" />, if any, that threw the current exception.</param>
public HttpUmbracoFormRouteStringException(string message, Exception innerException)
: base(message, innerException)
{ }
}
}
@@ -0,0 +1,62 @@
using System;
using System.Net;
using System.Net.Http;
using System.Web.Mvc;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Attribute used to check that the request contains a valid Umbraco form request string.
/// </summary>
/// <seealso cref="System.Web.Mvc.FilterAttribute" />
/// <seealso cref="System.Web.Mvc.IAuthorizationFilter" />
/// <remarks>
/// Applying this attribute/filter to a <see cref="SurfaceController"/> or SurfaceController Action will ensure that the Action can only be executed
/// when it is routed to from within Umbraco, typically when rendering a form with BegingUmbracoForm. It will mean that the natural MVC route for this Action
/// will fail with a <see cref="HttpUmbracoFormRouteStringException"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ValidateUmbracoFormRouteStringAttribute : FilterAttribute, IAuthorizationFilter
{
/// <summary>
/// Called when authorization is required.
/// </summary>
/// <param name="filterContext">The filter context.</param>
/// <exception cref="ArgumentNullException">filterContext</exception>
/// <exception cref="Umbraco.Web.Mvc.HttpUmbracoFormRouteStringException">The required request field \"ufprt\" is not present.
/// or
/// The Umbraco form request route string could not be decrypted.
/// or
/// The provided Umbraco form request route string was meant for a different controller and action.</exception>
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException(nameof(filterContext));
var ufprt = filterContext.HttpContext.Request["ufprt"];
ValidateRouteString(ufprt, filterContext.ActionDescriptor?.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor?.ActionName, filterContext.RouteData?.DataTokens["area"]?.ToString());
}
public void ValidateRouteString(string ufprt, string currentController, string currentAction, string currentArea)
{
if (ufprt.IsNullOrWhiteSpace())
{
throw new HttpUmbracoFormRouteStringException("The required request field \"ufprt\" is not present.");
}
if (!UmbracoHelper.DecryptAndValidateEncryptedRouteString(ufprt, out var additionalDataParts))
{
throw new HttpUmbracoFormRouteStringException("The Umbraco form request route string could not be decrypted.");
}
if (!additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Controller].InvariantEquals(currentController) ||
!additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Action].InvariantEquals(currentAction) ||
(!additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Area].IsNullOrWhiteSpace() && !additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Area].InvariantEquals(currentArea)))
{
throw new HttpUmbracoFormRouteStringException("The provided Umbraco form request route string was meant for a different controller and action.");
}
}
}
}
@@ -4,12 +4,12 @@ using Umbraco.Core;
using System.Web.Helpers;
using System.Web;
using Newtonsoft.Json;
using System.ComponentModel;
namespace Umbraco.Web.Security
{
/// <summary>
/// A custom <see cref="IAntiForgeryAdditionalDataProvider"/> to create a unique antiforgery token/validator per form created with BeginUmbracoForm
/// </summary>
[Obsolete("This is no longer used and will be removed from the codebase in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
public class UmbracoAntiForgeryAdditionalDataProvider : IAntiForgeryAdditionalDataProvider
{
private readonly IAntiForgeryAdditionalDataProvider _defaultProvider;
+3 -1
View File
@@ -307,7 +307,8 @@
<Compile Include="Cache\TemplateCacheRefresher.cs" />
<Compile Include="Cache\UnpublishedPageCacheRefresher.cs" />
<Compile Include="Cache\UserCacheRefresher.cs" />
<Compile Include="Security\UmbracoAntiForgeryAdditionalDataProvider.cs" />
<Compile Include="Mvc\HttpUmbracoFormRouteStringException.cs" />
<Compile Include="Mvc\ValidateUmbracoFormRouteStringAttribute.cs" />
<Compile Include="Editors\PreviewController.cs" />
<Compile Include="Editors\BackOfficeAssetsController.cs" />
<Compile Include="Features\DisabledFeatures.cs" />
@@ -340,6 +341,7 @@
<Compile Include="Models\Mapping\MemberTreeNodeUrlResolver.cs" />
<Compile Include="Models\Trees\ExportMember.cs" />
<Compile Include="PropertyEditors\DropdownFlexiblePropertyEditor.cs" />
<Compile Include="Security\UmbracoAntiForgeryAdditionalDataProvider.cs" />
<Compile Include="TourFilterResolver.cs" />
<Compile Include="Editors\UserEditorAuthorizationHelper.cs" />
<Compile Include="Editors\UserGroupAuthorizationAttribute.cs" />
+1 -1
View File
@@ -1656,7 +1656,7 @@ namespace Umbraco.Web
{
decryptedString = ufprt.DecryptWithMachineKey();
}
catch (FormatException)
catch (Exception ex) when (ex is FormatException || ex is ArgumentException)
{
LogHelper.Warn<UmbracoHelper>("A value was detected in the ufprt parameter but Umbraco could not decrypt the string");
parts = null;
-2
View File
@@ -190,8 +190,6 @@ namespace Umbraco.Web
base.Complete(afterComplete);
AntiForgeryConfig.AdditionalDataProvider = new UmbracoAntiForgeryAdditionalDataProvider(AntiForgeryConfig.AdditionalDataProvider);
//Now, startup all of our legacy startup handler
ApplicationEventsResolver.Current.InstantiateLegacyStartupHandlers();