This commit is contained in:
Shannon Deminick
2013-04-15 21:36:35 +06:00
12 changed files with 616 additions and 110 deletions

View File

@@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
internal class EntityService : IService
public class EntityService : IService
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly RepositoryFactory _repositoryFactory;

View File

@@ -0,0 +1,407 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
public class RelationService
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly RepositoryFactory _repositoryFactory;
private readonly EntityService _entityService;
public RelationService(IDatabaseUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory,
EntityService entityService)
{
_uowProvider = uowProvider;
_repositoryFactory = repositoryFactory;
_entityService = entityService;
}
/// <summary>
/// Gets a <see cref="Relation"/> by its Id
/// </summary>
/// <param name="id">Id of the <see cref="Relation"/></param>
/// <returns>A <see cref="Relation"/> object</returns>
public Relation GetById(int id)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
return repository.Get(id);
}
}
/// <summary>
/// Gets a <see cref="RelationType"/> by its Id
/// </summary>
/// <param name="id">Id of the <see cref="RelationType"/></param>
/// <returns>A <see cref="RelationType"/> object</returns>
public RelationType GetRelationTypeById(int id)
{
using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork()))
{
return repository.Get(id);
}
}
/// <summary>
/// Gets all <see cref="Relation"/> objects
/// </summary>
/// <param name="ids">Optional array of integer ids to return relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetAllRelations(params int[] ids)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
return repository.GetAll(ids);
}
}
/// <summary>
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>
/// </summary>
/// <param name="relationType"><see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetAllRelationsByRelationType(RelationType relationType)
{
return GetAllRelationsByRelationType(relationType.Id);
}
/// <summary>
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>'s Id
/// </summary>
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetAllRelationsByRelationType(int relationTypeId)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<Relation>().Where(x => x.RelationTypeId == relationTypeId);
return repository.GetByQuery(query);
}
}
/// <summary>
/// Gets all <see cref="Relation"/> objects
/// </summary>
/// <param name="ids">Optional array of integer ids to return relationtypes for</param>
/// <returns>An enumerable list of <see cref="RelationType"/> objects</returns>
public IEnumerable<RelationType> GetAllRelationTypes(params int[] ids)
{
using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork()))
{
return repository.GetAll(ids);
}
}
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their parent Id
/// </summary>
/// <param name="id">Id of the parent to retrieve relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetByParentId(int id)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<Relation>().Where(x => x.ParentId == id);
return repository.GetByQuery(query);
}
}
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their child Id
/// </summary>
/// <param name="id">Id of the child to retrieve relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetByChildId(int id)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<Relation>().Where(x => x.ChildId == id);
return repository.GetByQuery(query);
}
}
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by the Name of the <see cref="RelationType"/>
/// </summary>
/// <param name="relationTypeName">Name of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetByRelationTypeName(string relationTypeName)
{
List<int> relationTypeIds = null;
using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<RelationType>().Where(x => x.Name == relationTypeName);
var relationTypes = repository.GetByQuery(query);
if (relationTypes.Any())
{
relationTypeIds = relationTypes.Select(x => x.Id).ToList();
}
}
if (relationTypeIds == null)
return Enumerable.Empty<Relation>();
return GetRelationsByListOfTypeIds(relationTypeIds);
}
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by the Alias of the <see cref="RelationType"/>
/// </summary>
/// <param name="relationTypeAlias">Alias of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetByRelationTypeAlias(string relationTypeAlias)
{
List<int> relationTypeIds = null;
using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<RelationType>().Where(x => x.Alias == relationTypeAlias);
var relationTypes = repository.GetByQuery(query);
if (relationTypes.Any())
{
relationTypeIds = relationTypes.Select(x => x.Id).ToList();
}
}
if (relationTypeIds == null)
return Enumerable.Empty<Relation>();
return GetRelationsByListOfTypeIds(relationTypeIds);
}
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by the Id of the <see cref="RelationType"/>
/// </summary>
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
public IEnumerable<Relation> GetByRelationTypeId(int relationTypeId)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<Relation>().Where(x => x.RelationTypeId == relationTypeId);
return repository.GetByQuery(query);
}
}
/// <summary>
/// Gets the Child object from a Relation as an <see cref="IUmbracoEntity"/>
/// </summary>
/// <param name="relation">Relation to retrieve child object from</param>
/// <param name="loadBaseType">Optional bool to load the complete object graph when set to <c>False</c></param>
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public IUmbracoEntity GetChildEntityFromRelation(Relation relation, bool loadBaseType = false)
{
var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
return _entityService.Get(relation.ChildId, objectType, loadBaseType);
}
/// <summary>
/// Gets the Parent object from a Relation as an <see cref="IUmbracoEntity"/>
/// </summary>
/// <param name="relation">Relation to retrieve parent object from</param>
/// <param name="loadBaseType">Optional bool to load the complete object graph when set to <c>False</c></param>
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public IUmbracoEntity GetParentEntityFromRelation(Relation relation, bool loadBaseType = false)
{
var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
return _entityService.Get(relation.ParentId, objectType, loadBaseType);
}
/// <summary>
/// Gets the Parent and Child objects from a Relation as a <see cref="Tuple"/>"/> with <see cref="IUmbracoEntity"/>.
/// </summary>
/// <param name="relation">Relation to retrieve parent and child object from</param>
/// <param name="loadBaseType">Optional bool to load the complete object graph when set to <c>False</c></param>
/// <returns>Returns a Tuple with Parent (item1) and Child (item2)</returns>
public Tuple<IUmbracoEntity, IUmbracoEntity> GetEntitiesFromRelation(Relation relation, bool loadBaseType = false)
{
var childObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
var parentObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
var child = _entityService.Get(relation.ChildId, childObjectType, loadBaseType);
var parent = _entityService.Get(relation.ParentId, parentObjectType, loadBaseType);
return new Tuple<IUmbracoEntity, IUmbracoEntity>(parent, child);
}
/// <summary>
/// Gets the Child objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
/// </summary>
/// <param name="relations">List of relations to retrieve child objects from</param>
/// <param name="loadBaseType">Optional bool to load the complete object graph when set to <c>False</c></param>
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
public IEnumerable<IUmbracoEntity> GetChildEntitiesFromRelations(IEnumerable<Relation> relations, bool loadBaseType = false)
{
foreach (var relation in relations)
{
var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
yield return _entityService.Get(relation.ChildId, objectType, loadBaseType);
}
}
/// <summary>
/// Gets the Parent objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
/// </summary>
/// <param name="relations">List of relations to retrieve parent objects from</param>
/// <param name="loadBaseType">Optional bool to load the complete object graph when set to <c>False</c></param>
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
public IEnumerable<IUmbracoEntity> GetParentEntitiesFromRelations(IEnumerable<Relation> relations,
bool loadBaseType = false)
{
foreach (var relation in relations)
{
var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
yield return _entityService.Get(relation.ParentId, objectType, loadBaseType);
}
}
/// <summary>
/// Gets the Parent and Child objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
/// </summary>
/// <param name="relations">List of relations to retrieve parent and child objects from</param>
/// <param name="loadBaseType">Optional bool to load the complete object graph when set to <c>False</c></param>
/// <returns>An enumerable list of <see cref="Tuple"/> with <see cref="IUmbracoEntity"/></returns>
public IEnumerable<Tuple<IUmbracoEntity, IUmbracoEntity>> GetEntitiesFromRelations(
IEnumerable<Relation> relations,
bool loadBaseType = false)
{
foreach (var relation in relations)
{
var childObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
var parentObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
var child = _entityService.Get(relation.ChildId, childObjectType, loadBaseType);
var parent = _entityService.Get(relation.ParentId, parentObjectType, loadBaseType);
yield return new Tuple<IUmbracoEntity, IUmbracoEntity>(parent, child);
}
}
/// <summary>
/// Checks whether any relations exists for the passed in <see cref="RelationType"/>.
/// </summary>
/// <param name="relationType"><see cref="RelationType"/> to check for relations</param>
/// <returns>Returns <c>True</c> if any relations exists for the given <see cref="RelationType"/>, otherwise <c>False</c></returns>
public bool HasRelations(RelationType relationType)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<Relation>().Where(x => x.RelationTypeId == relationType.Id);
return repository.GetByQuery(query).Any();
}
}
/// <summary>
/// Checks whether any relations exists for the passed in Id.
/// </summary>
/// <param name="id">Id of an object to check relations for</param>
/// <returns>Returns <c>True</c> if any relations exists with the given Id, otherwise <c>False</c></returns>
public bool IsRelated(int id)
{
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
var query = new Query<Relation>().Where(x => x.ParentId == id || x.ChildId == id);
return repository.GetByQuery(query).Any();
}
}
/// <summary>
/// Saves a <see cref="Relation"/>
/// </summary>
/// <param name="relation">Relation to save</param>
public void Save(Relation relation)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateRelationRepository(uow))
{
repository.AddOrUpdate(relation);
uow.Commit();
}
}
/// <summary>
/// Saves a <see cref="RelationType"/>
/// </summary>
/// <param name="relationType">RelationType to Save</param>
public void Save(RelationType relationType)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateRelationTypeRepository(uow))
{
repository.AddOrUpdate(relationType);
uow.Commit();
}
}
/// <summary>
/// Deletes a <see cref="Relation"/>
/// </summary>
/// <param name="relation">Relation to Delete</param>
public void Delete(Relation relation)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateRelationRepository(uow))
{
repository.Delete(relation);
uow.Commit();
}
}
/// <summary>
/// Deletes a <see cref="RelationType"/>
/// </summary>
/// <param name="relationType">RelationType to Delete</param>
public void Delete(RelationType relationType)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateRelationTypeRepository(uow))
{
repository.Delete(relationType);
uow.Commit();
}
}
/// <summary>
/// Deletes all <see cref="Relation"/> objects based on the passed in <see cref="RelationType"/>
/// </summary>
/// <param name="relationType"><see cref="RelationType"/> to Delete Relations for</param>
public void DeleteRelationsOfType(RelationType relationType)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateRelationRepository(uow))
{
var query = new Query<Relation>().Where(x => x.RelationTypeId == relationType.Id);
var list = repository.GetByQuery(query).ToList();
foreach (var relation in list)
{
repository.Delete(relation);
}
uow.Commit();
}
}
#region Private Methods
private IEnumerable<Relation> GetRelationsByListOfTypeIds(IEnumerable<int> relationTypeIds)
{
var relations = new List<Relation>();
using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork()))
{
foreach (var relationTypeId in relationTypeIds)
{
int id = relationTypeId;
var query = new Query<Relation>().Where(x => x.RelationTypeId == id);
relations.AddRange(repository.GetByQuery(query).ToList());
}
}
return relations;
}
#endregion
}
}

View File

@@ -22,6 +22,7 @@ namespace Umbraco.Core.Services
private Lazy<PackagingService> _packagingService;
private Lazy<ServerRegistrationService> _serverRegistrationService;
private Lazy<EntityService> _entityService;
private Lazy<RelationService> _relationService;
/// <summary>
/// Constructor
@@ -78,6 +79,9 @@ namespace Umbraco.Core.Services
if (_entityService == null)
_entityService = new Lazy<EntityService>(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value));
if(_relationService == null)
_relationService = new Lazy<RelationService>(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value));
}
/// <summary>
@@ -91,11 +95,19 @@ namespace Umbraco.Core.Services
/// <summary>
/// Gets the <see cref="EntityService"/>
/// </summary>
internal EntityService EntityService
public EntityService EntityService
{
get { return _entityService.Value; }
}
/// <summary>
/// Gets the <see cref="RelationService"/>
/// </summary>
public RelationService RelationService
{
get { return _relationService.Value; }
}
/// <summary>
/// Gets the <see cref="IContentService"/>
/// </summary>

View File

@@ -17,7 +17,7 @@ namespace Umbraco.Core.Strings
/// <summary>
/// Flag mask for casing.
/// </summary>
CaseMask = 0x1f, // 0xff - 8 possible values
CaseMask = 0x3f, // 0xff - 8 possible values
/// <summary>
/// Flag mask for encoding.
@@ -59,6 +59,13 @@ namespace Umbraco.Core.Strings
/// </summary>
UpperCase = 0x10,
/// <summary>
/// Umbraco "safe alias" case.
/// </summary>
/// <remarks>This is for backward compatibility. Casing is unchanged within terms,
/// and is pascal otherwise.</remarks>
UmbracoCase = 0x20,
/// <summary>
/// Unicode encoding.
/// </summary>

View File

@@ -144,10 +144,23 @@ namespace Umbraco.Core.Strings
}
public Func<string, string> PreFilter { get; private set; }
// indicate whether an uppercase within a term eg "fooBar" is to break
// into a new term, or to be considered as part of the current term
public bool BreakTermsOnUpper { get; private set; }
// indicates whether it is legal to have leading digits, or whether they
// should be stripped as any other illegal character
public bool AllowLeadingDigits { get; private set; }
// indicates whether underscore is a valid character in a term or is
// to be considered as a separator
public bool AllowUnderscoreInTerm { get; private set; }
// indicates whether acronyms parsing is greedy ie whether "FOObar" is
// "FOO" + "bar" (greedy) or "FO" + "Obar" (non-greedy)
public bool GreedyAcronyms { get { return false; } }
public static readonly HelperConfig Empty = new HelperConfig();
}
@@ -239,7 +252,7 @@ function validateSafeAlias(id, value, immediate, callback) {{
/// </remarks>
public virtual string CleanStringForSafeAlias(string text)
{
return CleanString(text, CleanStringType.Ascii | CleanStringType.CamelCase | CleanStringType.Alias);
return CleanString(text, CleanStringType.Ascii | CleanStringType.UmbracoCase | CleanStringType.Alias);
}
/// <summary>
@@ -253,7 +266,7 @@ function validateSafeAlias(id, value, immediate, callback) {{
/// </remarks>
public virtual string CleanStringForSafeAlias(string text, CultureInfo culture)
{
return CleanString(text, CleanStringType.Ascii | CleanStringType.CamelCase | CleanStringType.Alias, culture);
return CleanString(text, CleanStringType.Ascii | CleanStringType.UmbracoCase | CleanStringType.Alias, culture);
}
/// <summary>
@@ -720,6 +733,8 @@ function validateSafeAlias(id, value, immediate, callback) {{
case StateAcronym:
if (!isTerm || isLower || isDigit)
{
if (isLower && !config.GreedyAcronyms)
i -= 1;
CopyUtf8Term(input, ipos, output, ref opos, i - ipos, caseType, culture, /*termFilter,*/ true);
ipos = i;
state = isTerm ? StateWord : StateBreak;
@@ -780,9 +795,9 @@ function validateSafeAlias(id, value, immediate, callback) {{
if (isAcronym)
{
if (caseType == CleanStringType.CamelCase && len <= 2 && opos > 0)
caseType = CleanStringType.Unchanged;
else if (caseType == CleanStringType.PascalCase && len <= 2)
if ((caseType == CleanStringType.CamelCase && len <= 2 && opos > 0) ||
(caseType == CleanStringType.PascalCase && len <= 2) ||
(caseType == CleanStringType.UmbracoCase))
caseType = CleanStringType.Unchanged;
}
@@ -822,6 +837,14 @@ function validateSafeAlias(id, value, immediate, callback) {{
opos += len - 1;
break;
case CleanStringType.UmbracoCase:
c = term[ipos++];
output[opos] = opos++ == 0 ? c : char.ToUpper(c, culture);
if (len > 1)
term.CopyTo(ipos, output, opos, len - 1);
opos += len - 1;
break;
default:
throw new ArgumentOutOfRangeException("caseType");
}

View File

@@ -681,6 +681,7 @@
<Compile Include="Services\IUserService.cs" />
<Compile Include="Services\LocalizationService.cs" />
<Compile Include="Services\MediaService.cs" />
<Compile Include="Services\RelationService.cs" />
<Compile Include="Services\ServerRegistrationService.cs" />
<Compile Include="Services\PackagingService.cs" />
<Compile Include="Services\ServiceContext.cs" />

View File

@@ -64,19 +64,19 @@ namespace Umbraco.Tests.CoreStrings
#region Cases
[TestCase("foo", "foo")]
[TestCase(" foo ", "foo")]
[TestCase("Foo", "foo")]
[TestCase("FoO", "foO")]
[TestCase("FoO bar", "foOBar")]
[TestCase("FoO bar NIL", "foOBarNil")]
[TestCase("FoO 33bar 22NIL", "foO33bar22Nil")]
[TestCase("FoO 33bar 22NI", "foO33bar22NI")]
[TestCase("Foo", "Foo")]
[TestCase("FoO", "FoO")]
[TestCase("FoO bar", "FoOBar")]
[TestCase("FoO bar NIL", "FoOBarNIL")]
[TestCase("FoO 33bar 22NIL", "FoO33bar22NIL")]
[TestCase("FoO 33bar 22NI", "FoO33bar22NI")]
[TestCase("0foo", "foo")]
[TestCase("2foo bar", "fooBar")]
[TestCase("9FOO", "foo")]
[TestCase("foo-BAR", "fooBar")]
[TestCase("9FOO", "FOO")]
[TestCase("foo-BAR", "fooBAR")]
[TestCase("foo-BA-dang", "fooBADang")]
[TestCase("foo_BAR", "fooBar")]
[TestCase("foo'BAR", "fooBar")]
[TestCase("foo_BAR", "fooBAR")]
[TestCase("foo'BAR", "fooBAR")]
[TestCase("sauté dans l'espace", "sauteDansLespace")]
[TestCase("foo\"\"bar", "fooBar")]
[TestCase("-foo-", "foo")]
@@ -85,10 +85,14 @@ namespace Umbraco.Tests.CoreStrings
[TestCase("brô dëk ", "broDek")]
[TestCase("1235brô dëk ", "broDek")]
[TestCase("汉#字*/漢?字", "")]
[TestCase("aa DB cd EFG X KLMN OP qrst", "aaDBCdEfgXKlmnOPQrst")]
[TestCase("AA db cd EFG X KLMN OP qrst", "aaDbCdEfgXKlmnOPQrst")]
[TestCase("AAA db cd EFG X KLMN OP qrst", "aaaDbCdEfgXKlmnOPQrst")]
[TestCase("aa DB cd EFG X KLMN OP qrst", "aaDBCdEFGXKLMNOPQrst")]
[TestCase("AA db cd EFG X KLMN OP qrst", "AADbCdEFGXKLMNOPQrst")]
[TestCase("AAA db cd EFG X KLMN OP qrst", "AAADbCdEFGXKLMNOPQrst")]
[TestCase("4 ways selector", "waysSelector")]
[TestCase("WhatIfWeDoItAgain", "WhatIfWeDoItAgain")]
[TestCase("whatIfWeDoItAgain", "whatIfWeDoItAgain")]
[TestCase("WhatIfWEDOITAgain", "WhatIfWEDOITAgain")]
[TestCase("WhatIfWe doItAgain", "WhatIfWeDoItAgain")]
#endregion
public void CleanStringForSafeAlias(string input, string expected)
{
@@ -244,8 +248,8 @@ namespace Umbraco.Tests.CoreStrings
#region Cases
[TestCase("1235brô dëK tzARlan ban123!pOo", "brodeKtzARlanban123pOo", CleanStringType.Unchanged)]
[TestCase(" 1235brô dëK tzARlan ban123!pOo ", "brodeKtzARlanban123pOo", CleanStringType.Unchanged)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "BroDeKTzARLanBan123POo", CleanStringType.PascalCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "broDeKTzARLanBan123POo", CleanStringType.CamelCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "BroDeKTzARlanBan123POo", CleanStringType.PascalCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "broDeKTzARlanBan123POo", CleanStringType.CamelCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "BRODEKTZARLANBAN123POO", CleanStringType.UpperCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "brodektzarlanban123poo", CleanStringType.LowerCase)]
[TestCase("aa DB cd EFG X KLMN OP qrst", "aaDBCdEfgXKlmnOPQrst", CleanStringType.CamelCase)]
@@ -277,10 +281,12 @@ namespace Umbraco.Tests.CoreStrings
[TestCase("Räksmörgås %%$£¤¤¤§ kéKé", "RaksmorgaskeKe", CleanStringType.Unchanged)]
[TestCase("TRii", "TRii", CleanStringType.Unchanged)]
[TestCase("**TRii", "TRii", CleanStringType.Unchanged)]
[TestCase("TRii", "trIi", CleanStringType.CamelCase)]
[TestCase("**TRii", "trIi", CleanStringType.CamelCase)]
[TestCase("TRii", "TRIi", CleanStringType.PascalCase)]
[TestCase("**TRii", "TRIi", CleanStringType.PascalCase)]
[TestCase("TRii", "tRii", CleanStringType.CamelCase)]
[TestCase("TRXii", "trXii", CleanStringType.CamelCase)]
[TestCase("**TRii", "tRii", CleanStringType.CamelCase)]
[TestCase("TRii", "TRii", CleanStringType.PascalCase)]
[TestCase("TRXii", "TRXii", CleanStringType.PascalCase)]
[TestCase("**TRii", "TRii", CleanStringType.PascalCase)]
[TestCase("trII", "trII", CleanStringType.Unchanged)]
[TestCase("**trII", "trII", CleanStringType.Unchanged)]
[TestCase("trII", "trII", CleanStringType.CamelCase)]
@@ -299,14 +305,14 @@ namespace Umbraco.Tests.CoreStrings
}
#region Cases
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro de K tz AR lan ban123 p Oo", ' ', CleanStringType.Unchanged)]
[TestCase(" 1235brô dëK tzARlan ban123!pOo ", "bro de K tz AR lan ban123 p Oo", ' ', CleanStringType.Unchanged)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "Bro De K Tz AR Lan Ban123 P Oo", ' ', CleanStringType.PascalCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "Bro De K Tz AR Lan Ban123 P Oo", ' ', CleanStringType.PascalCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro De K Tz AR Lan Ban123 P Oo", ' ', CleanStringType.CamelCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro-De-K-Tz-AR-Lan-Ban123-P-Oo", '-', CleanStringType.CamelCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "BRO-DE-K-TZ-AR-LAN-BAN123-P-OO", '-', CleanStringType.UpperCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro-de-k-tz-ar-lan-ban123-p-oo", '-', CleanStringType.LowerCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro de K tz A Rlan ban123 p Oo", ' ', CleanStringType.Unchanged)]
[TestCase(" 1235brô dëK tzARlan ban123!pOo ", "bro de K tz A Rlan ban123 p Oo", ' ', CleanStringType.Unchanged)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "Bro De K Tz A Rlan Ban123 P Oo", ' ', CleanStringType.PascalCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "Bro De K Tz A Rlan Ban123 P Oo", ' ', CleanStringType.PascalCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro De K Tz A Rlan Ban123 P Oo", ' ', CleanStringType.CamelCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro-De-K-Tz-A-Rlan-Ban123-P-Oo", '-', CleanStringType.CamelCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "BRO-DE-K-TZ-A-RLAN-BAN123-P-OO", '-', CleanStringType.UpperCase)]
[TestCase("1235brô dëK tzARlan ban123!pOo", "bro-de-k-tz-a-rlan-ban123-p-oo", '-', CleanStringType.LowerCase)]
[TestCase("Tab 1", "tab 1", ' ', CleanStringType.CamelCase)]
[TestCase("Home - Page", "home Page", ' ', CleanStringType.CamelCase)]
[TestCase("Shannon's Document Type", "shannon S Document Type", ' ', CleanStringType.CamelCase)]

View File

@@ -46,6 +46,11 @@ namespace Umbraco.Tests.CoreStrings
[TestCase("aa DB cd EFG X KLMN OP qrst", "aaDBCdEFGXKLMNOPQrst")]
[TestCase("AA db cd EFG X KLMN OP qrst", "AADbCdEFGXKLMNOPQrst")]
[TestCase("AAA db cd EFG X KLMN OP qrst", "AAADbCdEFGXKLMNOPQrst")]
[TestCase("4 ways selector", "WaysSelector")]
[TestCase("WhatIfWeDoItAgain", "WhatIfWeDoItAgain")]
[TestCase("whatIfWeDoItAgain", "whatIfWeDoItAgain")]
[TestCase("WhatIfWEDOITAgain", "WhatIfWEDOITAgain")]
[TestCase("WhatIfWe doItAgain", "WhatIfWeDoItAgain")]
#endregion
public void CleanStringForSafeAlias(string input, string expected)
{

View File

@@ -14,7 +14,9 @@ namespace Umbraco.Web.Routing
/// by one specified template, using one specified Culture and RenderingEngine.
/// </summary>
public class PublishedContentRequest
{
{
private bool _readonly;
/// <summary>
/// Triggers once the published content request has been prepared, but before it is processed.
/// </summary>
@@ -74,6 +76,11 @@ namespace Umbraco.Web.Routing
{
if (Prepared != null)
Prepared(this, EventArgs.Empty);
if (!HasPublishedContent)
Is404 = true; // safety
_readonly = true;
}
/// <summary>
@@ -82,6 +89,12 @@ namespace Umbraco.Web.Routing
/// <remarks>The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc.</remarks>
public Uri Uri { get; private set; }
private void EnsureWriteable()
{
if (_readonly)
throw new InvalidOperationException("Cannot modify a PublishedContentRequest once it is read-only.");
}
#region PublishedContent
/// <summary>
@@ -105,6 +118,7 @@ namespace Umbraco.Web.Routing
get { return _publishedContent; }
set
{
EnsureWriteable();
_publishedContent = value;
IsInternalRedirectPublishedContent = false;
TemplateModel = null;
@@ -119,6 +133,8 @@ namespace Umbraco.Web.Routing
/// preserve or reset the template, if any.</remarks>
public void SetInternalRedirectPublishedContent(IPublishedContent content)
{
EnsureWriteable();
// unless a template has been set already by the finder,
// template should be null at that point.
var initial = IsInitialPublishedContent;
@@ -152,6 +168,8 @@ namespace Umbraco.Web.Routing
/// </summary>
public void SetIsInitialPublishedContent()
{
EnsureWriteable();
// note: it can very well be null if the initial content was not found
_initialPublishedContent = _publishedContent;
IsInternalRedirectPublishedContent = false;
@@ -217,11 +235,13 @@ namespace Umbraco.Web.Routing
/// <param name="alias">The alias of the template.</param>
/// <returns>A value indicating whether a valid template with the specified alias was found.</returns>
/// <remarks>
/// <para>Successfully setting the template resets <c>RenderingEngine</c> to <c>Unknown</c>.</para>
/// <para>Successfully setting the template does refresh <c>RenderingEngine</c>.</para>
/// <para>If setting the template fails, then the previous template (if any) remains in place.</para>
/// </remarks>
public bool TrySetTemplate(string alias)
{
EnsureWriteable();
if (string.IsNullOrWhiteSpace(alias))
{
TemplateModel = null;
@@ -239,6 +259,17 @@ namespace Umbraco.Web.Routing
return true;
}
/// <summary>
/// Sets the template to use to display the requested content.
/// </summary>
/// <param name="template">The template.</param>
/// <remarks>Setting the template does refresh <c>RenderingEngine</c>.</remarks>
public void SetTemplate(ITemplate template)
{
EnsureWriteable();
TemplateModel = template;
}
/// <summary>
/// Gets a value indicating whether the content request has a template.
/// </summary>
@@ -270,10 +301,20 @@ namespace Umbraco.Web.Routing
get { return Domain != null; }
}
/// <summary>
/// Gets or sets the content request's culture.
/// </summary>
public CultureInfo Culture { get; set; }
private CultureInfo _culture;
/// <summary>
/// Gets or sets the content request's culture.
/// </summary>
public CultureInfo Culture
{
get { return _culture; }
set
{
EnsureWriteable();
_culture = value;
}
}
// note: do we want to have an ordered list of alternate cultures,
// to allow for fallbacks when doing dictionnary lookup and such?
@@ -332,6 +373,7 @@ namespace Umbraco.Web.Routing
/// where we want to allow developers to indicate a request is 404 but not to cancel it.</remarks>
public void SetIs404()
{
EnsureWriteable();
Is404 = true;
}
@@ -358,6 +400,7 @@ namespace Umbraco.Web.Routing
/// redirect. Redirect will or will not take place in due time.</remarks>
public void SetRedirect(string url)
{
EnsureWriteable();
RedirectUrl = url;
IsRedirectPermanent = false;
}
@@ -370,6 +413,7 @@ namespace Umbraco.Web.Routing
/// redirect. Redirect will or will not take place in due time.</remarks>
public void SetRedirectPermanent(string url)
{
EnsureWriteable();
RedirectUrl = url;
IsRedirectPermanent = true;
}
@@ -383,6 +427,8 @@ namespace Umbraco.Web.Routing
/// redirect. Redirect will or will not take place in due time.</remarks>
public void SetRedirect(string url, int status)
{
EnsureWriteable();
if (status < 300 || status > 308)
throw new ArgumentOutOfRangeException("status", "Valid redirection status codes 300-308.");
@@ -416,6 +462,8 @@ namespace Umbraco.Web.Routing
/// not be used, in due time.</remarks>
public void SetResponseStatus(int code, string description = null)
{
EnsureWriteable();
// .Status is deprecated
// .SubStatusCode is IIS 7+ internal, ignore
ResponseStatusCode = code;

View File

@@ -74,28 +74,22 @@ namespace Umbraco.Web.Routing
// trigger the Prepared event - at that point it is still possible to change about anything
// even though the request might be flagged for redirection - we'll redirect _after_ the event
//
// also, OnPrepared() will make the PublishedContentRequest readonly, so nothing can change
//
_pcr.OnPrepared();
// we don't take care of anything xcept finding the rendering engine again
// so if the content has changed, it's up to the user to find out the template
// we don't take care of anything so if the content has changed, it's up to the user
// to find out the appropriate template
// set the culture on the thread -- again, 'cos it might have changed in the event handler
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = _pcr.Culture;
// if request has been flagged to redirect then return
// whoever called us is in charge of actually redirecting
if (_pcr.IsRedirect)
// if request has been flagged to redirect, or has no content to display,
// then return - whoever called us is in charge of actually redirecting
if (_pcr.IsRedirect || !_pcr.HasPublishedContent)
return;
if (!_pcr.HasPublishedContent)
{
// safety
_pcr.Is404 = true;
// whoever called us is in charge of doing what's appropriate
return;
}
// we may be 404 _and_ have a content
// can't go beyond that point without a PublishedContent to render

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Security;
using Umbraco.Core;
@@ -34,51 +33,53 @@ namespace Umbraco.Web.Security
IEnumerable<string> allowGroups = null,
IEnumerable<int> allowMembers = null)
{
if (allowAll)
return true;
if (allowTypes == null)
allowTypes = Enumerable.Empty<string>();
if (allowGroups == null)
allowGroups = Enumerable.Empty<string>();
if (allowMembers == null)
allowMembers = Enumerable.Empty<int>();
// Allow by default
var allowAction = true;
// If not set to allow all, need to check current loggined in member
if (!allowAll)
// Get member details
var member = Member.GetCurrentMember();
if (member == null)
{
// Get member details
var member = Member.GetCurrentMember();
if (member == null)
// If not logged on, not allowed
allowAction = false;
}
else
{
// If types defined, check member is of one of those types
var allowTypesList = allowTypes as IList<string> ?? allowTypes.ToList();
if (allowTypesList.Any(allowType => allowType != string.Empty))
{
// If not logged on, not allowed
allowAction = false;
// Allow only if member's type is in list
allowAction = allowTypesList.Select(x => x.ToLowerInvariant()).Contains(member.ContentType.Alias.ToLowerInvariant());
}
else
// If groups defined, check member is of one of those groups
var allowGroupsList = allowGroups as IList<string> ?? allowGroups.ToList();
if (allowAction && allowGroupsList.Any(allowGroup => allowGroup != string.Empty))
{
// If types defined, check member is of one of those types
if (allowTypes.Any())
{
// Allow only if member's type is in list
allowAction = allowTypes.Select(x => x.ToLowerInvariant()).Contains(member.ContentType.Alias.ToLowerInvariant());
}
// Allow only if member's type is in list
var groups = Roles.GetRolesForUser(member.LoginName);
allowAction = groups.Select(s => s.ToLowerInvariant()).Intersect(groups.Select(myGroup => myGroup.ToLowerInvariant())).Any();
}
// If groups defined, check member is of one of those groups
if (allowAction && allowGroups.Any())
{
// Allow only if member's type is in list
var groups = System.Web.Security.Roles.GetRolesForUser(member.LoginName);
allowAction = groups.Select(s => s.ToLower()).Intersect(allowGroups).Any();
}
// If specific members defined, check member is of one of those
if (allowAction && allowMembers.Any())
{
// Allow only if member's type is in list
allowAction = allowMembers.Contains(member.Id);
}
// If specific members defined, check member is of one of those
if (allowAction && allowMembers.Any())
{
// Allow only if member's type is in list
allowAction = allowMembers.Contains(member.Id);
}
}
return allowAction;
}
@@ -92,7 +93,7 @@ namespace Umbraco.Web.Security
}
private const long TicksPrMinute = 600000000;
private static readonly int UmbracoTimeOutInMinutes = Core.Configuration.GlobalSettings.TimeOutInMinutes;
private static readonly int UmbracoTimeOutInMinutes = GlobalSettings.TimeOutInMinutes;
private User _currentUser;
@@ -122,7 +123,7 @@ namespace Umbraco.Web.Security
var retVal = Guid.NewGuid();
SqlHelper.ExecuteNonQuery(
"insert into umbracoUserLogins (contextID, userID, timeout) values (@contextId,'" + userId + "','" +
(DateTime.Now.Ticks + (TicksPrMinute * UmbracoTimeOutInMinutes)).ToString() +
(DateTime.Now.Ticks + (TicksPrMinute * UmbracoTimeOutInMinutes)) +
"') ",
SqlHelper.CreateParameter("@contextId", retVal));
UmbracoUserContextId = retVal.ToString();
@@ -167,7 +168,8 @@ namespace Umbraco.Web.Security
/// <returns></returns>
internal bool ValidateBackOfficeCredentials(string username, string password)
{
return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].ValidateUser(username, password);
var membershipProvider = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider];
return membershipProvider != null && membershipProvider.ValidateUser(username, password);
}
/// <summary>
@@ -180,7 +182,7 @@ namespace Umbraco.Web.Security
internal bool ValidateUserNodeTreePermissions(User umbracoUser, string path, string action)
{
var permissions = umbracoUser.GetPermissions(path);
if (permissions.IndexOf(action) > -1 && (path.Contains("-20") || ("," + path + ",").Contains("," + umbracoUser.StartNodeId.ToString() + ",")))
if (permissions.IndexOf(action, StringComparison.Ordinal) > -1 && (path.Contains("-20") || ("," + path + ",").Contains("," + umbracoUser.StartNodeId + ",")))
return true;
var user = umbracoUser;
@@ -232,7 +234,7 @@ namespace Umbraco.Web.Security
SqlHelper.CreateParameter("@contextId", new Guid(UmbracoUserContextId))
);
}
return GetTimeout(UmbracoUserContextId);
}
@@ -244,21 +246,21 @@ namespace Umbraco.Web.Security
public int GetUserId(string umbracoUserContextId)
{
//need to parse to guid
Guid gid;
if (!Guid.TryParse(umbracoUserContextId, out gid))
Guid guid;
if (Guid.TryParse(umbracoUserContextId, out guid) == false)
{
return -1;
}
var id = ApplicationContext.Current.ApplicationCache.GetCacheItem<int?>(
var id = ApplicationContext.Current.ApplicationCache.GetCacheItem(
CacheKeys.UserContextCacheKey + umbracoUserContextId,
new TimeSpan(0, UmbracoTimeOutInMinutes/10, 0),
new TimeSpan(0, UmbracoTimeOutInMinutes / 10, 0),
() => SqlHelper.ExecuteScalar<int?>(
"select userID from umbracoUserLogins where contextID = @contextId",
SqlHelper.CreateParameter("@contextId", gid)));
SqlHelper.CreateParameter("@contextId", guid)));
if (id == null)
return -1;
return id.Value;
return id.Value;
}
/// <summary>
@@ -301,7 +303,7 @@ namespace Umbraco.Web.Security
var user = User.GetUser(uid);
// Check for console access
if (user.Disabled || (user.NoConsole && GlobalSettings.RequestIsInUmbracoApplication(httpContext) && !GlobalSettings.RequestIsLiveEditRedirector(httpContext)))
if (user.Disabled || (user.NoConsole && GlobalSettings.RequestIsInUmbracoApplication(httpContext) && GlobalSettings.RequestIsLiveEditRedirector(httpContext) == false))
{
if (throwExceptions) throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator");
return ValidateRequestAttempt.FailedNoPrivileges;
@@ -325,9 +327,9 @@ namespace Umbraco.Web.Security
internal ValidateRequestAttempt AuthorizeRequest(HttpContextBase httpContext, bool throwExceptions = false)
{
// check for secure connection
if (GlobalSettings.UseSSL && !httpContext.Request.IsSecureConnection)
if (GlobalSettings.UseSSL && httpContext.Request.IsSecureConnection == false)
{
if (throwExceptions) throw new UserAuthorizationException("This installation requires a secure connection (via SSL). Please update the URL to include https://");
if (throwExceptions) throw new UserAuthorizationException("This installation requires a secure connection (via SSL). Please update the URL to include https://");
return ValidateRequestAttempt.FailedNoSsl;
}
return ValidateCurrentUser(httpContext, throwExceptions);
@@ -377,10 +379,14 @@ namespace Umbraco.Web.Security
try
{
var encTicket = StateHelper.Cookies.UserContext.GetValue();
if (!string.IsNullOrEmpty(encTicket))
return FormsAuthentication.Decrypt(encTicket).UserData;
if (string.IsNullOrEmpty(encTicket) == false)
{
var formsAuthenticationTicket = FormsAuthentication.Decrypt(encTicket);
if (formsAuthenticationTicket != null)
return formsAuthenticationTicket.UserData;
}
}
catch (HttpException ex)
catch (HttpException)
{
// we swallow this type of exception as it happens if a legacy (pre 4.8.1) cookie is set
}
@@ -400,7 +406,7 @@ namespace Umbraco.Web.Security
if (StateHelper.Cookies.UserContext.HasValue)
StateHelper.Cookies.ClearAll();
if (!String.IsNullOrEmpty(value))
if (string.IsNullOrEmpty(value) == false)
{
var ticket = new FormsAuthenticationTicket(1,
value,
@@ -411,13 +417,10 @@ namespace Umbraco.Web.Security
FormsAuthentication.FormsCookiePath);
// Encrypt the ticket.
var encTicket = FormsAuthentication.Encrypt(ticket);
FormsAuthentication.Encrypt(ticket);
// Create new cookie.
StateHelper.Cookies.UserContext.SetValue(value, 1);
}
else
{

View File

@@ -274,7 +274,7 @@ namespace Umbraco.Web
/// <summary>
/// Gets/sets the PublishedContentRequest object
/// </summary>
internal PublishedContentRequest PublishedContentRequest { get; set; }
public PublishedContentRequest PublishedContentRequest { get; set; }
/// <summary>
/// Exposes the HttpContext for the current request