Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9865596dcc | |||
| dc44a7a3a7 | |||
| ff595df7ca | |||
| 686f6752f3 | |||
| 23ccdeb2cf | |||
| 6148ce4e52 | |||
| 06850ed25c | |||
| 0320a56cb5 | |||
| cd27bb210f | |||
| 67f680a675 | |||
| 47c3e3a79f | |||
| f3bfc1ffb2 | |||
| 89cd655df2 | |||
| 14c4c4815d | |||
| bfb69a34ef | |||
| d52420183e | |||
| 2aaca865e7 | |||
| 59bf24c461 | |||
| 2748c3635d | |||
| 2252db0d55 |
+2
-2
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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: []
|
||||
}
|
||||
|
||||
+9
-3
@@ -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];
|
||||
|
||||
|
||||
+10
-4
@@ -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);
|
||||
}
|
||||
|
||||
+2
-1
@@ -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: []
|
||||
}
|
||||
|
||||
+38
-37
@@ -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;
|
||||
|
||||
+5
-4
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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,
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+6
-2
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user