diff --git a/.gitignore b/.gitignore index 4e2183af5c..2fcab792e3 100644 --- a/.gitignore +++ b/.gitignore @@ -144,5 +144,5 @@ build/docs.zip build/ui-docs.zip build/csharp-docs.zip build/msbuild.log - +.vs/ src/packages/ \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 7e2bb88964..d7f4576137 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -122,6 +122,11 @@ namespace Umbraco.Core /// MediaType alias for an image. /// public const string Image = "Image"; + + /// + /// MediaType alias indicating allowing auto-selection. + /// + public const string AutoSelect = "umbracoAutoSelect"; } /// diff --git a/src/Umbraco.Core/CoreRuntimeComponent.cs b/src/Umbraco.Core/CoreRuntimeComponent.cs index 476c3a43d6..68ed71f484 100644 --- a/src/Umbraco.Core/CoreRuntimeComponent.cs +++ b/src/Umbraco.Core/CoreRuntimeComponent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using AutoMapper; using LightInject; @@ -71,11 +72,16 @@ namespace Umbraco.Core // register a server registrar, by default it's the db registrar unless the dev // has the legacy dist calls enabled - fixme - should obsolete the legacy thing - composition.Container.RegisterSingleton(factory => UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled - ? (IServerRegistrar)new ConfigServerRegistrar(UmbracoConfig.For.UmbracoSettings()) - : (IServerRegistrar)new DatabaseServerRegistrar( - new Lazy(factory.GetInstance), - new DatabaseServerRegistrarOptions())); + composition.Container.RegisterSingleton(f => + { + if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled) + return new ConfigServerRegistrar(UmbracoConfig.For.UmbracoSettings()); + if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"])) + return new SingleServerRegistrar(f.GetInstance()); + return new DatabaseServerRegistrar( + new Lazy(f.GetInstance), + new DatabaseServerRegistrarOptions()); + }); // by default we'll use the database server messenger with default options (no callbacks), // this will be overridden by either the legacy thing or the db thing in the corresponding diff --git a/src/Umbraco.Core/DI/RepositoryCompositionRoot.cs b/src/Umbraco.Core/DI/RepositoryCompositionRoot.cs index 638ca84656..d7975db96e 100644 --- a/src/Umbraco.Core/DI/RepositoryCompositionRoot.cs +++ b/src/Umbraco.Core/DI/RepositoryCompositionRoot.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.IO; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Plugins; @@ -26,6 +27,9 @@ namespace Umbraco.Core.DI container.RegisterSingleton(); container.RegisterSingleton(); + // register query factory + container.RegisterSingleton(); + // register repository factory container.RegisterSingleton(); diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index a0b9bfa8f1..b822b66504 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -326,7 +326,7 @@ namespace Umbraco.Core.Models } } - /// + /// /// A boolean flag indicating if a property type has been removed from this instance. /// /// diff --git a/src/Umbraco.Core/Models/PublicAccessEntry.cs b/src/Umbraco.Core/Models/PublicAccessEntry.cs index 969e853f05..1ed15a8fb4 100644 --- a/src/Umbraco.Core/Models/PublicAccessEntry.cs +++ b/src/Umbraco.Core/Models/PublicAccessEntry.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Models NoAccessNodeId = noAccessNode.Id; _protectedNodeId = protectedNode.Id; - _ruleCollection = new ObservableCollection(ruleCollection); + _ruleCollection = new ObservableCollection(ruleCollection); _ruleCollection.CollectionChanged += _ruleCollection_CollectionChanged; } @@ -81,7 +81,7 @@ namespace Umbraco.Core.Models internal IEnumerable RemovedRules { get { return _removedRules; } - } + } public IEnumerable Rules { @@ -123,7 +123,7 @@ namespace Umbraco.Core.Models get { return _noAccessNodeId; } set { SetPropertyValueAndDetectChanges(value, ref _noAccessNodeId, Ps.Value.NoAccessNodeIdSelector); } } - + [DataMember] public int ProtectedNodeId { diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs index e2134c952e..fe90bd9fff 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs @@ -12,5 +12,21 @@ /// The strongly-typed model representing the published content, or the published content /// itself it the factory has no model for that content type. IPublishedContent CreateModel(IPublishedContent content); + + T CreateModel(IPublishedFragment content); + + // fixme + // and we'd need a + // PublishedContentModel = ContentModel : ContentWrapper + // PublishedFragmentModel = FragmentModel : FragmentWrapper + // + // ModelFactory.Meta.Model("thing").ClrType (find the our post?) + // + // then + // make a plan to get NestedContent in + // and an equivalent of Vorto with different syntax + // + // then + // VARIANTS ARCHITECTURE FFS! } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedContentModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedContentModelFactory.cs index a92427191e..9fbcadb29b 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedContentModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedContentModelFactory.cs @@ -6,5 +6,10 @@ { return content; } + + public T CreateModel(IPublishedFragment content) + { + throw new System.NotImplementedException(); + } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs index 17c01b4785..d3d6b43170 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs @@ -81,5 +81,10 @@ namespace Umbraco.Core.Models.PublishedContent ? constructor(content) : content; } + + public T CreateModel(IPublishedFragment content) + { + throw new NotImplementedException(); + } } } diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs index b385381e5b..c79daaf43d 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs @@ -12,7 +12,6 @@ namespace Umbraco.Core.Models.Rdbms { [Column("id")] [PrimaryKeyColumn] - [Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyData")] public int Id { get; set; } [Column("contentNodeId")] diff --git a/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs b/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs index d6ad4f1f3c..a3e2eeea68 100644 --- a/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs +++ b/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence { ValidateDatabase(database); - database.Execute("UPDATE umbracoNode SET sortOrder = (CASE WHEN (sortOrder=1) THEN -1 ELSE 1 END) WHERE id=@id", + database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { @id = nodeId }); } @@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence { ValidateDatabase(database); - database.ExecuteScalar("SELECT sortOrder FROM umbracoNode WHERE id=@id", + database.ExecuteScalar("SELECT value FROM umbracoLock WHERE id=@id", new { @id = nodeId }); } } diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs index df45d87e75..d0b44f7ae2 100644 --- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs @@ -28,7 +28,6 @@ namespace Umbraco.Core.Persistence /// internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory { - private readonly IMapperCollection _mappers; private readonly IUmbracoDatabaseAccessor _umbracoDatabaseAccessor; private readonly ISqlSyntaxProvider[] _sqlSyntaxProviders; private readonly ILogger _logger; @@ -42,7 +41,6 @@ namespace Umbraco.Core.Persistence private ISqlSyntaxProvider _sqlSyntax; private RetryPolicy _connectionRetryPolicy; private RetryPolicy _commandRetryPolicy; - private IQueryFactory _queryFactory; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); /// @@ -51,10 +49,10 @@ namespace Umbraco.Core.Persistence /// The collection of available sql syntax providers. /// A logger. /// - /// + /// /// Used by LightInject. - public DefaultDatabaseFactory(IEnumerable sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IMapperCollection mappers) - : this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger, umbracoDatabaseAccessor, mappers) + public DefaultDatabaseFactory(IEnumerable sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IQueryFactory queryFactory) + : this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger, umbracoDatabaseAccessor, queryFactory) { if (Configured == false) DatabaseContext.GiveLegacyAChance(this, logger); @@ -67,17 +65,17 @@ namespace Umbraco.Core.Persistence /// The collection of available sql syntax providers. /// A logger /// - /// + /// /// Used by the other ctor and in tests. - public DefaultDatabaseFactory(string connectionStringName, IEnumerable sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IMapperCollection mappers) + public DefaultDatabaseFactory(string connectionStringName, IEnumerable sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IQueryFactory queryFactory) { if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders)); if (logger == null) throw new ArgumentNullException(nameof(logger)); if (umbracoDatabaseAccessor == null) throw new ArgumentNullException(nameof(umbracoDatabaseAccessor)); if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentNullOrEmptyException(nameof(connectionStringName)); - if (mappers == null) throw new ArgumentNullException(nameof(mappers)); + if (queryFactory == null) throw new ArgumentNullException(nameof(queryFactory)); - _mappers = mappers; + QueryFactory = queryFactory; _sqlSyntaxProviders = sqlSyntaxProviders.ToArray(); _logger = logger; _umbracoDatabaseAccessor = umbracoDatabaseAccessor; @@ -107,16 +105,16 @@ namespace Umbraco.Core.Persistence /// The collection of available sql syntax providers. /// A logger. /// - /// + /// /// Used in tests. - public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IMapperCollection mappers) + public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IQueryFactory queryFactory) { if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders)); if (logger == null) throw new ArgumentNullException(nameof(logger)); if (umbracoDatabaseAccessor == null) throw new ArgumentNullException(nameof(umbracoDatabaseAccessor)); - if (mappers == null) throw new ArgumentNullException(nameof(mappers)); + if (queryFactory == null) throw new ArgumentNullException(nameof(queryFactory)); - _mappers = mappers; + QueryFactory = queryFactory; _sqlSyntaxProviders = sqlSyntaxProviders.ToArray(); _logger = logger; _umbracoDatabaseAccessor = umbracoDatabaseAccessor; @@ -156,14 +154,7 @@ namespace Umbraco.Core.Persistence /// /// Gets the database query factory. /// - public IQueryFactory QueryFactory - { - get - { - EnsureConfigured(); - return _queryFactory ?? (_queryFactory = new QueryFactory(SqlSyntax, _mappers)); - } - } + public IQueryFactory QueryFactory { get; } // will be configured by the database context public void Configure(string connectionString, string providerName) diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 26a14d2477..c9aa6f9775 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -39,6 +39,11 @@ namespace Umbraco.Core.Persistence.Migrations.Initial CreateUmbracoLockData(); } + if (tableName.Equals("umbracoLock")) + { + CreateUmbracoLockData(); + } + if (tableName.Equals("cmsContentType")) { CreateCmsContentTypeData(); @@ -165,8 +170,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateCmsContentTypeData() { _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "icon-folder", IsContainer = false, AllowAtRoot = true }); - _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "icon-picture" }); - _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "icon-document" }); + _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "icon-picture", AllowAtRoot = true }); + _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "icon-document", AllowAtRoot = true }); _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "icon-user" }); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockObjects.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockObjects.cs new file mode 100644 index 0000000000..8ee31f4b07 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockObjects.cs @@ -0,0 +1,37 @@ +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive +{ + [Migration("7.5.5", 101, GlobalSettings.UmbracoMigrationName)] + public class AddLockObjects : MigrationBase + { + public AddLockObjects(IMigrationContext context) + : base(context) + { } + + public override void Up() + { + EnsureLockObject(Constants.Locks.Servers, "Servers"); + } + + public override void Down() + { + // not implemented + } + + private void EnsureLockObject(int id, string name) + { + Execute.Code(db => + { + var exists = db.Exists(id); + if (exists) return string.Empty; + // be safe: delete old umbracoNode lock objects if any + db.Execute("DELETE FROM umbracoNode WHERE id=@id;", new { id }); + // then create umbracoLock object + db.Execute("INSERT umbracoLock (id, name, value) VALUES (@id, @name, 1);", new { id, name }); + return string.Empty; + }); + } + } +} diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockTable.cs new file mode 100644 index 0000000000..8d7f2ed8fb --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockTable.cs @@ -0,0 +1,30 @@ +using System.Linq; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive +{ + [Migration("7.5.5", 100, GlobalSettings.UmbracoMigrationName)] + public class AddLockTable : MigrationBase + { + public AddLockTable(IMigrationContext context) + : base(context) + { } + + public override void Up() + { + var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray(); + if (tables.InvariantContains("umbracoLock") == false) + { + Create.Table("umbracoLock") + .WithColumn("id").AsInt32().PrimaryKey("PK_umbracoLock") + .WithColumn("value").AsInt32().NotNullable() + .WithColumn("name").AsString(64).NotNullable(); + } + } + + public override void Down() + { + // not implemented + } + } +} diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/UpdateAllowedMediaTypesAtRoot.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/UpdateAllowedMediaTypesAtRoot.cs new file mode 100644 index 0000000000..5a95b9b8bf --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/UpdateAllowedMediaTypesAtRoot.cs @@ -0,0 +1,25 @@ +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive +{ + /// + /// See: http://issues.umbraco.org/issue/U4-4196 + /// + [Migration("7.5.5", 1, GlobalSettings.UmbracoMigrationName)] + public class UpdateAllowedMediaTypesAtRoot : MigrationBase + { + public UpdateAllowedMediaTypesAtRoot(IMigrationContext context) + : base(context) + { } + + public override void Up() + { + Execute.Sql("UPDATE cmsContentType SET allowAtRoot = 1 WHERE nodeId = 1032 OR nodeId = 1033"); + } + + public override void Down() + { } + } +} diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs new file mode 100644 index 0000000000..dabba48334 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero +{ + /// + /// See: http://issues.umbraco.org/issue/U4-9188 + /// + [Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)] + public class UpdateUniqueIndexOnCmsPropertyData : MigrationBase + { + public UpdateUniqueIndexOnCmsPropertyData(IMigrationContext context) + : base(context) + { + } + + public override void Up() + { + //tuple = tablename, indexname, columnname, unique + var indexes = SqlSyntax.GetDefinedIndexes(Context.Database).ToArray(); + var found = indexes.FirstOrDefault( + x => x.Item1.InvariantEquals("cmsPropertyData") + && x.Item2.InvariantEquals("IX_cmsPropertyData")); + + if (found != null) + { + //drop the index + Delete.Index("IX_cmsPropertyData").OnTable("cmsPropertyData"); + } + } + + public override void Down() + { + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs index d16cf7fe0f..d001358a97 100644 --- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence public static Sql Where(this Sql sql, Expression> predicate) { - var expresionist = new PocoToSqlExpressionHelper(sql.SqlContext); + var expresionist = new PocoToSqlExpressionVisitor(sql.SqlContext); var whereExpression = expresionist.Visit(predicate); sql.Where(whereExpression, expresionist.GetSqlParameters()); return sql; diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ExpressionVisitorBase.cs similarity index 63% rename from src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs rename to src/Umbraco.Core/Persistence/Querying/ExpressionVisitorBase.cs index 560e6f6eeb..9a7962230d 100644 --- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ExpressionVisitorBase.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Text; @@ -13,27 +12,117 @@ namespace Umbraco.Core.Persistence.Querying { // fixme.npoco - are we basically duplicating entire parts of NPoco just because of SqlSyntax ?! - internal abstract class BaseExpressionHelper : BaseExpressionHelper + /// + /// Represents an expression which caches the visitor's result. + /// + internal class CachedExpression : Expression { - protected BaseExpressionHelper(ISqlSyntaxProvider sqlSyntax) - : base(sqlSyntax) + private string _visitResult; + + /// + /// Gets or sets the inner Expression. + /// + public Expression InnerExpression { get; private set; } + + /// + /// Gets or sets the compiled SQL statement output. + /// + public string VisitResult { + get { return _visitResult; } + set + { + if (Visited) + throw new InvalidOperationException("Cached expression has already been visited."); + _visitResult = value; + Visited = true; + } } - protected abstract string VisitMemberAccess(MemberExpression m); + /// + /// Gets or sets a value indicating whether the cache Expression has been compiled already. + /// + public bool Visited { get; private set; } - protected internal virtual string Visit(Expression exp) + /// + /// Replaces the inner expression. + /// + /// expression. + /// The new expression is assumed to have different parameter but produce the same SQL statement. + public void Wrap(Expression expression) { + InnerExpression = expression; + } + } - if (exp == null) return string.Empty; - switch (exp.NodeType) + /// + /// An expression tree parser to create SQL statements and SQL parameters based on a strongly typed expression. + /// + /// This object is stateful and cannot be re-used to parse an expression. + internal abstract class ExpressionVisitorBase + { + protected ExpressionVisitorBase(ISqlSyntaxProvider sqlSyntax) + { + SqlSyntax = sqlSyntax; + } + + /// + /// Gets or sets a value indicating whether the visited expression has been visited already, + /// in which case visiting will just populate the SQL parameters. + /// + protected bool Visited { get; set; } + + /// + /// Gets or sets the SQL syntax provider for the current database. + /// + protected ISqlSyntaxProvider SqlSyntax { get; private set; } + + /// + /// Gets the list of SQL parameters. + /// + protected readonly List SqlParameters = new List(); + + /// + /// Gets the SQL parameters. + /// + /// + public object[] GetSqlParameters() + { + return SqlParameters.ToArray(); + } + + /// + /// Visits the expression and produces the corresponding SQL statement. + /// + /// The expression + /// The SQL statement corresponding to the expression. + /// Also populates the SQL parameters. + public virtual string Visit(Expression expression) + { + // if the expression is a CachedExpression, + // visit the inner expression if not already visited + var cachedExpression = expression as CachedExpression; + if (cachedExpression != null) + { + Visited = cachedExpression.Visited; + expression = cachedExpression.InnerExpression; + } + + if (expression == null) return string.Empty; + + string result; + + switch (expression.NodeType) { case ExpressionType.Lambda: - return VisitLambda(exp as LambdaExpression); + result = VisitLambda(expression as LambdaExpression); + break; case ExpressionType.MemberAccess: - return VisitMemberAccess(exp as MemberExpression); + result = VisitMemberAccess(expression as MemberExpression); + break; case ExpressionType.Constant: - return VisitConstant(exp as ConstantExpression); + result = VisitConstant(expression as ConstantExpression); + break; case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.Subtract: @@ -57,7 +146,8 @@ namespace Umbraco.Core.Persistence.Querying case ExpressionType.RightShift: case ExpressionType.LeftShift: case ExpressionType.ExclusiveOr: - return VisitBinary(exp as BinaryExpression); + result = VisitBinary(expression as BinaryExpression); + break; case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: @@ -66,35 +156,54 @@ namespace Umbraco.Core.Persistence.Querying case ExpressionType.ArrayLength: case ExpressionType.Quote: case ExpressionType.TypeAs: - return VisitUnary(exp as UnaryExpression); + result = VisitUnary(expression as UnaryExpression); + break; case ExpressionType.Parameter: - return VisitParameter(exp as ParameterExpression); + result = VisitParameter(expression as ParameterExpression); + break; case ExpressionType.Call: - return VisitMethodCall(exp as MethodCallExpression); + result = VisitMethodCall(expression as MethodCallExpression); + break; case ExpressionType.New: - return VisitNew(exp as NewExpression); + result = VisitNew(expression as NewExpression); + break; case ExpressionType.NewArrayInit: case ExpressionType.NewArrayBounds: - return VisitNewArray(exp as NewArrayExpression); + result = VisitNewArray(expression as NewArrayExpression); + break; default: - return exp.ToString(); + result = expression.ToString(); + break; } + + // if the expression is a CachedExpression, + // and is not already compiled, assign the result + if (cachedExpression != null) + { + if (cachedExpression.Visited == false) + cachedExpression.VisitResult = result; + result = cachedExpression.VisitResult; + } + + return result; } + protected abstract string VisitMemberAccess(MemberExpression m); + protected virtual string VisitLambda(LambdaExpression lambda) { if (lambda.Body.NodeType == ExpressionType.MemberAccess) { var m = lambda.Body as MemberExpression; - if (m.Expression != null) + if (m != null && m.Expression != null) { //This deals with members that are boolean (i.e. x => IsTrashed ) - string r = VisitMemberAccess(m); - SqlParameters.Add(true); - return string.Format("{0} = @{1}", r, SqlParameters.Count - 1); + var r = VisitMemberAccess(m); - //return string.Format("{0}={1}", r, GetQuotedTrueValue()); + SqlParameters.Add(true); + + return Visited ? string.Empty : string.Format("{0} = @{1}", r, SqlParameters.Count - 1); } } @@ -103,19 +212,24 @@ namespace Umbraco.Core.Persistence.Querying protected virtual string VisitBinary(BinaryExpression b) { - string left, right; + var left = string.Empty; + var right = string.Empty; + var operand = BindOperant(b.NodeType); if (operand == "AND" || operand == "OR") { - MemberExpression m = b.Left as MemberExpression; + var m = b.Left as MemberExpression; if (m != null && m.Expression != null) { string r = VisitMemberAccess(m); SqlParameters.Add(1); - left = string.Format("{0} = @{1}", r, SqlParameters.Count - 1); - //left = string.Format("{0}={1}", r, GetQuotedTrueValue()); + //don't execute if compiled + if (Visited == false) + { + left = string.Format("{0} = @{1}", r, SqlParameters.Count - 1); + } } else { @@ -124,12 +238,15 @@ namespace Umbraco.Core.Persistence.Querying m = b.Right as MemberExpression; if (m != null && m.Expression != null) { - string r = VisitMemberAccess(m); + var r = VisitMemberAccess(m); SqlParameters.Add(1); - right = string.Format("{0} = @{1}", r, SqlParameters.Count - 1); - //right = string.Format("{0}={1}", r, GetQuotedTrueValue()); + //don't execute if compiled + if (Visited == false) + { + right = string.Format("{0} = @{1}", r, SqlParameters.Count - 1); + } } else { @@ -140,14 +257,14 @@ namespace Umbraco.Core.Persistence.Querying { // deal with (x == true|false) - most common var constRight = b.Right as ConstantExpression; - if (constRight != null && constRight.Type == typeof (bool)) - return ((bool) constRight.Value) ? VisitNotNot(b.Left) : VisitNot(b.Left); + if (constRight != null && constRight.Type == typeof(bool)) + return (bool)constRight.Value ? VisitNotNot(b.Left) : VisitNot(b.Left); right = Visit(b.Right); // deal with (true|false == x) - why not var constLeft = b.Left as ConstantExpression; - if (constLeft != null && constLeft.Type == typeof (bool)) - return ((bool) constLeft.Value) ? VisitNotNot(b.Right) : VisitNot(b.Right); + if (constLeft != null && constLeft.Type == typeof(bool)) + return (bool)constLeft.Value ? VisitNotNot(b.Right) : VisitNot(b.Right); left = Visit(b.Left); } else if (operand == "<>") @@ -155,13 +272,13 @@ namespace Umbraco.Core.Persistence.Querying // deal with (x != true|false) - most common var constRight = b.Right as ConstantExpression; if (constRight != null && constRight.Type == typeof (bool)) - return ((bool) constRight.Value) ? VisitNot(b.Left) : VisitNotNot(b.Left); + return (bool)constRight.Value ? VisitNot(b.Left) : VisitNotNot(b.Left); right = Visit(b.Right); // deal with (true|false != x) - why not var constLeft = b.Left as ConstantExpression; if (constLeft != null && constLeft.Type == typeof (bool)) - return ((bool) constLeft.Value) ? VisitNot(b.Right) : VisitNotNot(b.Right); + return (bool)constLeft.Value ? VisitNot(b.Right) : VisitNotNot(b.Right); left = Visit(b.Left); } else @@ -186,26 +303,38 @@ namespace Umbraco.Core.Persistence.Querying { case "MOD": case "COALESCE": - return string.Format("{0}({1},{2})", operand, left, right); + //don't execute if compiled + if (Visited == false) + { + return string.Format("{0}({1},{2})", operand, left, right); + } + //already compiled, return + return string.Empty; default: - return "(" + left + " " + operand + " " + right + ")"; + //don't execute if compiled + if (Visited == false) + { + return string.Concat("(", left, " ", operand, " ", right, ")"); + } + //already compiled, return + return string.Empty; } } - protected virtual List VisitExpressionList(ReadOnlyCollection original) + protected virtual List VisitExpressionList(ReadOnlyCollection original) { - var list = new List(); + var list = new List(); for (int i = 0, n = original.Count; i < n; i++) { if (original[i].NodeType == ExpressionType.NewArrayInit || - original[i].NodeType == ExpressionType.NewArrayBounds) + original[i].NodeType == ExpressionType.NewArrayBounds) { - list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression)); } else + { list.Add(Visit(original[i])); - + } } return list; } @@ -218,27 +347,37 @@ namespace Umbraco.Core.Persistence.Querying try { var getter = lambda.Compile(); - object o = getter(); + var o = getter(); SqlParameters.Add(o); - return string.Format("@{0}", SqlParameters.Count - 1); - //return GetQuotedValue(o, o.GetType()); + //don't execute if compiled + if (Visited == false) + { + return string.Format("@{0}", SqlParameters.Count - 1); + } + //already compiled, return + return string.Empty; } catch (InvalidOperationException) { - // FieldName ? - List exprs = VisitExpressionList(nex.Arguments); - var r = new StringBuilder(); - foreach (Object e in exprs) + //don't execute if compiled + if (Visited == false) { - r.AppendFormat("{0}{1}", - r.Length > 0 ? "," : "", - e); + // FieldName ? + List exprs = VisitExpressionList(nex.Arguments); + var r = new StringBuilder(); + foreach (var e in exprs) + { + r.AppendFormat("{0}{1}", + r.Length > 0 ? "," : "", + e); + } + return r.ToString(); } - return r.ToString(); + //already compiled, return + return string.Empty; } - } protected virtual string VisitParameter(ParameterExpression p) @@ -252,14 +391,14 @@ namespace Umbraco.Core.Persistence.Querying return "null"; SqlParameters.Add(c.Value); - return string.Format("@{0}", SqlParameters.Count - 1); - //if (c.Value is bool) - //{ - // object o = GetQuotedValue(c.Value, c.Value.GetType()); - // return string.Format("({0}={1})", GetQuotedTrueValue(), o); - //} - //return GetQuotedValue(c.Value, c.Value.GetType()); + //don't execute if compiled + if (Visited == false) + { + return string.Format("@{0}", SqlParameters.Count - 1); + } + //already compiled, return + return string.Empty; } protected virtual string VisitUnary(UnaryExpression u) @@ -285,10 +424,22 @@ namespace Umbraco.Core.Persistence.Querying case ExpressionType.MemberAccess: // false property , i.e. x => !Trashed SqlParameters.Add(true); - return string.Format("NOT ({0} = @{1})", o, SqlParameters.Count - 1); + //don't execute if compiled + if (Visited == false) + { + return string.Format("NOT ({0} = @{1})", o, SqlParameters.Count - 1); + } + //already compiled, return + return string.Empty; default: - // could be anything else, such as: x => !x.Path.StartsWith("-20") - return "NOT (" + o + ")"; + //don't execute if compiled + if (Visited == false) + { + // could be anything else, such as: x => !x.Path.StartsWith("-20") + return string.Concat("NOT (", o, ")"); + } + //already compiled, return + return string.Empty; } } @@ -301,7 +452,14 @@ namespace Umbraco.Core.Persistence.Querying case ExpressionType.MemberAccess: // true property, i.e. x => Trashed SqlParameters.Add(true); - return string.Format("({0} = @{1})", o, SqlParameters.Count - 1); + + //don't execute if compiled + if (Visited == false) + { + return string.Format("({0} = @{1})", o, SqlParameters.Count - 1); + } + //already compiled, return + return string.Empty; default: // could be anything else, such as: x => x.Path.StartsWith("-20") return o; @@ -310,27 +468,31 @@ namespace Umbraco.Core.Persistence.Querying protected virtual string VisitNewArray(NewArrayExpression na) { + var exprs = VisitExpressionList(na.Expressions); - List exprs = VisitExpressionList(na.Expressions); - var r = new StringBuilder(); - foreach (Object e in exprs) + //don't execute if compiled + if (Visited == false) { - r.Append(r.Length > 0 ? "," + e : e); - } + var r = new StringBuilder(); + foreach (var e in exprs) + { + r.Append(r.Length > 0 ? "," + e : e); + } - return r.ToString(); + return r.ToString(); + } + //already compiled, return + return string.Empty; } - protected virtual List VisitNewArrayFromExpressionList(NewArrayExpression na) + protected virtual List VisitNewArrayFromExpressionList(NewArrayExpression na) { - - List exprs = VisitExpressionList(na.Expressions); + var exprs = VisitExpressionList(na.Expressions); return exprs; } protected virtual string BindOperant(ExpressionType e) { - switch (e) { case ExpressionType.Equal: @@ -369,16 +531,16 @@ namespace Umbraco.Core.Persistence.Querying protected virtual string VisitMethodCall(MethodCallExpression m) { //Here's what happens with a MethodCallExpression: - // If a method is called that contains a single argument, + // If a method is called that contains a single argument, // then m.Object is the object on the left hand side of the method call, example: // x.Path.StartsWith(content.Path) // m.Object = x.Path // and m.Arguments.Length == 1, therefor m.Arguments[0] == content.Path - // If a method is called that contains multiple arguments, then m.Object == null and the + // If a method is called that contains multiple arguments, then m.Object == null and the // m.Arguments collection contains the left hand side of the method call, example: // x.Path.SqlStartsWith(content.Path, TextColumnType.NVarchar) // m.Object == null - // m.Arguments.Length == 3, therefor, m.Arguments[0] == x.Path, m.Arguments[1] == content.Path, m.Arguments[2] == TextColumnType.NVarchar + // m.Arguments.Length == 3, therefor, m.Arguments[0] == x.Path, m.Arguments[1] == content.Path, m.Arguments[2] == TextColumnType.NVarchar // So, we need to cater for these scenarios. var objectForMethod = m.Object ?? m.Arguments[0]; @@ -391,11 +553,23 @@ namespace Umbraco.Core.Persistence.Querying { case "ToString": SqlParameters.Add(objectForMethod.ToString()); - return string.Format("@{0}", SqlParameters.Count - 1); + //don't execute if compiled + if (Visited == false) + return string.Format("@{0}", SqlParameters.Count - 1); + //already compiled, return + return string.Empty; case "ToUpper": - return string.Format("upper({0})", visitedObjectForMethod); + //don't execute if compiled + if (Visited == false) + return string.Format("upper({0})", visitedObjectForMethod); + //already compiled, return + return string.Empty; case "ToLower": - return string.Format("lower({0})", visitedObjectForMethod); + //don't execute if compiled + if (Visited == false) + return string.Format("lower({0})", visitedObjectForMethod); + //already compiled, return + return string.Empty; case "SqlWildcard": case "StartsWith": case "EndsWith": @@ -496,7 +670,12 @@ namespace Umbraco.Core.Persistence.Querying SqlParameters.Add(RemoveQuote(replaceValue)); - return string.Format("replace({0}, @{1}, @{2})", visitedObjectForMethod, SqlParameters.Count - 2, SqlParameters.Count - 1); + //don't execute if compiled + if (Visited == false) + return string.Format("replace({0}, @{1}, @{2})", visitedObjectForMethod, SqlParameters.Count - 2, SqlParameters.Count - 1); + //already compiled, return + return string.Empty; + //case "Substring": // var startIndex = Int32.Parse(args[0].ToString()) + 1; // if (args.Count == 2) @@ -563,87 +742,28 @@ namespace Umbraco.Core.Persistence.Querying throw new ArgumentOutOfRangeException("No logic supported for " + m.Method.Name); - //var s2 = new StringBuilder(); - //foreach (Object e in args) - //{ - // s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType())); - //} - //return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString()); + //var s2 = new StringBuilder(); + //foreach (Object e in args) + //{ + // s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType())); + //} + //return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString()); } } public virtual string GetQuotedTableName(string tableName) { - return string.Format("\"{0}\"", tableName); + return Visited ? tableName : string.Format("\"{0}\"", tableName); } public virtual string GetQuotedColumnName(string columnName) { - return string.Format("\"{0}\"", columnName); + return Visited ? columnName : string.Format("\"{0}\"", columnName); } public virtual string GetQuotedName(string name) { - return string.Format("\"{0}\"", name); - } - - //private string GetQuotedTrueValue() - //{ - // return GetQuotedValue(true, typeof(bool)); - //} - - //private string GetQuotedFalseValue() - //{ - // return GetQuotedValue(false, typeof(bool)); - //} - - //public virtual string GetQuotedValue(object value, Type fieldType) - //{ - // return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue); - //} - - //private string GetTrueExpression() - //{ - // object o = GetQuotedTrueValue(); - // return string.Format("({0}={1})", o, o); - //} - - //private string GetFalseExpression() - //{ - - // return string.Format("({0}={1})", - // GetQuotedTrueValue(), - // GetQuotedFalseValue()); - //} - - //private bool IsTrueExpression(string exp) - //{ - // return (exp == GetTrueExpression()); - //} - - //private bool IsFalseExpression(string exp) - //{ - // return (exp == GetFalseExpression()); - //} - } - - /// - /// Logic that is shared with the expression helpers - /// - internal abstract class BaseExpressionHelper - { - public ISqlSyntaxProvider SqlSyntax { get; private set; } - - protected BaseExpressionHelper(ISqlSyntaxProvider sqlSyntax) - { - SqlSyntax = sqlSyntax; - } - - protected List SqlParameters = new List(); - - public object[] GetSqlParameters() - { - return SqlParameters.ToArray(); + return Visited ? name : string.Format("\"{0}\"", name); } protected string HandleStringComparison(string col, string val, string verb, TextColumnType columnType) @@ -652,25 +772,45 @@ namespace Umbraco.Core.Persistence.Querying { case "SqlWildcard": SqlParameters.Add(RemoveQuote(val)); - return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //don't execute if compiled + if (Visited == false) + return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //already compiled, return + return string.Empty; case "Equals": SqlParameters.Add(RemoveQuote(val)); - return SqlSyntax.GetStringColumnEqualComparison(col, SqlParameters.Count - 1, columnType); + //don't execute if compiled + if (Visited == false) + return SqlSyntax.GetStringColumnEqualComparison(col, SqlParameters.Count - 1, columnType); + //already compiled, return + return string.Empty; case "StartsWith": SqlParameters.Add(string.Format("{0}{1}", RemoveQuote(val), SqlSyntax.GetWildcardPlaceholder())); - return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //don't execute if compiled + if (Visited == false) + return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //already compiled, return + return string.Empty; case "EndsWith": SqlParameters.Add(string.Format("{0}{1}", SqlSyntax.GetWildcardPlaceholder(), RemoveQuote(val))); - return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //don't execute if compiled + if (Visited == false) + return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //already compiled, return + return string.Empty; case "Contains": SqlParameters.Add(string.Format("{0}{1}{0}", SqlSyntax.GetWildcardPlaceholder(), RemoveQuote(val))); - return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //don't execute if compiled + if (Visited == false) + return SqlSyntax.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); + //already compiled, return + return string.Empty; case "InvariantEquals": case "SqlEquals": //recurse @@ -755,16 +895,9 @@ namespace Umbraco.Core.Persistence.Querying protected virtual string RemoveQuote(string exp) { - //if (exp.StartsWith("'") && exp.EndsWith("'")) - //{ - // exp = exp.Remove(0, 1); - // exp = exp.Remove(exp.Length - 1, 1); - //} - //return exp; - if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'")) - && - (exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'"))) + && + (exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'"))) { exp = exp.Remove(0, 1); exp = exp.Remove(exp.Length - 1, 1); @@ -772,17 +905,17 @@ namespace Umbraco.Core.Persistence.Querying return exp; } - //protected virtual string RemoveQuoteFromAlias(string exp) + //protected virtual string RemoveQuoteFromAlias(string expression) //{ - // if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'")) + // if ((expression.StartsWith("\"") || expression.StartsWith("`") || expression.StartsWith("'")) // && - // (exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'"))) + // (expression.EndsWith("\"") || expression.EndsWith("`") || expression.EndsWith("'"))) // { - // exp = exp.Remove(0, 1); - // exp = exp.Remove(exp.Length - 1, 1); + // expression = expression.Remove(0, 1); + // expression = expression.Remove(expression.Length - 1, 1); // } - // return exp; + // return expression; //} } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/IQuery.cs b/src/Umbraco.Core/Persistence/Querying/IQuery.cs index a85e865c49..88b337a634 100644 --- a/src/Umbraco.Core/Persistence/Querying/IQuery.cs +++ b/src/Umbraco.Core/Persistence/Querying/IQuery.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; namespace Umbraco.Core.Persistence.Querying { - /// /// Represents a query for building Linq translatable SQL queries /// diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs deleted file mode 100644 index d3c3067ab7..0000000000 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.Persistence.SqlSyntax; - -namespace Umbraco.Core.Persistence.Querying -{ - internal class ModelToSqlExpressionHelper : BaseExpressionHelper - { - private readonly BaseMapper _mapper; - - public ModelToSqlExpressionHelper(ISqlSyntaxProvider sqlSyntax, BaseMapper mapper) - : base(sqlSyntax) - { - if (mapper == null) throw new ArgumentNullException("mapper"); - _mapper = mapper; - } - - public ModelToSqlExpressionHelper(ISqlSyntaxProvider sqlSyntax, IMapperCollection mappers) - : base(sqlSyntax) - { - _mapper = mappers[typeof(T)]; // throws if not found - } - - protected override string VisitMemberAccess(MemberExpression m) - { - if (m.Expression != null && - m.Expression.NodeType == ExpressionType.Parameter - && m.Expression.Type == typeof(T)) - { - var field = _mapper.Map(SqlSyntax, m.Member.Name, true); - if (field.IsNullOrWhiteSpace()) - throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name); - return field; - } - - if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) - { - var field = _mapper.Map(SqlSyntax, m.Member.Name, true); - if (field.IsNullOrWhiteSpace()) - throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name); - return field; - } - - var member = Expression.Convert(m, typeof(object)); - var lambda = Expression.Lambda>(member); - var getter = lambda.Compile(); - object o = getter(); - - SqlParameters.Add(o); - return string.Format("@{0}", SqlParameters.Count - 1); - - //return GetQuotedValue(o, o != null ? o.GetType() : null); - - } - - //protected bool IsFieldName(string quotedExp) - //{ - // //Not entirely sure this is reliable, but its better then simply returning true - // return quotedExp.LastIndexOf("'", StringComparison.InvariantCultureIgnoreCase) + 1 != quotedExp.Length; - //} - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionVisitor.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionVisitor.cs new file mode 100644 index 0000000000..94ac8444a4 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionVisitor.cs @@ -0,0 +1,80 @@ +using System; +using System.Linq.Expressions; +using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Querying +{ + /// + /// An expression tree parser to create SQL statements and SQL parameters based on a strongly typed expression, + /// based on Umbraco's business logic models. + /// + /// This object is stateful and cannot be re-used to parse an expression. + internal class ModelToSqlExpressionVisitor : ExpressionVisitorBase + { + private readonly BaseMapper _mapper; + + public ModelToSqlExpressionVisitor(ISqlSyntaxProvider sqlSyntax, BaseMapper mapper) + : base(sqlSyntax) + { + if (mapper == null) throw new ArgumentNullException(nameof(mapper)); + _mapper = mapper; + } + + public ModelToSqlExpressionVisitor(ISqlSyntaxProvider sqlSyntax, IMapperCollection mappers) + : base(sqlSyntax) + { + _mapper = mappers[typeof(T)]; // throws if not found + } + + protected override string VisitMemberAccess(MemberExpression m) + { + if (m.Expression != null && + m.Expression.NodeType == ExpressionType.Parameter + && m.Expression.Type == typeof(T)) + { + //don't execute if compiled + if (Visited == false) + { + var field = _mapper.Map(SqlSyntax, m.Member.Name, true); + if (field.IsNullOrWhiteSpace()) + throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name); + return field; + } + + //already compiled, return + return string.Empty; + } + + if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) + { + //don't execute if compiled + if (Visited == false) + { + var field = _mapper.Map(SqlSyntax, m.Member.Name, true); + if (field.IsNullOrWhiteSpace()) + throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name); + return field; + } + + //already compiled, return + return string.Empty; + } + + var member = Expression.Convert(m, typeof(object)); + var lambda = Expression.Lambda>(member); + var getter = lambda.Compile(); + var o = getter(); + + SqlParameters.Add(o); + + //don't execute if compiled + if (Visited == false) + return $"@{SqlParameters.Count - 1}"; + + //already compiled, return + return string.Empty; + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs similarity index 50% rename from src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs rename to src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs index 958854727a..f60cadbde5 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs @@ -1,51 +1,69 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using NPoco; - -namespace Umbraco.Core.Persistence.Querying -{ - internal class PocoToSqlExpressionHelper : BaseExpressionHelper - { - private readonly PocoData _pd; - - public PocoToSqlExpressionHelper(SqlContext sqlContext) - : base(sqlContext.SqlSyntax) - { - _pd = sqlContext.PocoDataFactory.ForType(typeof (T)); - } - - protected override string VisitMemberAccess(MemberExpression m) - { - if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof (T)) - { - var field = GetFieldName(_pd, m.Member.Name); - return field; - } - - if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) - { - var field = GetFieldName(_pd, m.Member.Name); - return field; - } - - var member = Expression.Convert(m, typeof(object)); - var lambda = Expression.Lambda>(member); - var getter = lambda.Compile(); - var o = getter(); - - SqlParameters.Add(o); - - return $"@{SqlParameters.Count - 1}"; - } - - protected virtual string GetFieldName(PocoData pocoData, string name) - { - var column = pocoData.Columns.FirstOrDefault(x => x.Value.MemberInfoData.Name == name); - var tableName = SqlSyntax.GetQuotedTableName(pocoData.TableInfo.TableName); - var columnName = SqlSyntax.GetQuotedColumnName(column.Value.ColumnName); - - return $"{tableName}.{columnName}"; - } - } +using System; +using System.Linq; +using System.Linq.Expressions; +using NPoco; + +namespace Umbraco.Core.Persistence.Querying +{ + /// + /// An expression tree parser to create SQL statements and SQL parameters based on a strongly typed expression, + /// based on Umbraco's DTOs. + /// + /// This object is stateful and cannot be re-used to parse an expression. + internal class PocoToSqlExpressionVisitor : ExpressionVisitorBase + { + private readonly PocoData _pd; + + public PocoToSqlExpressionVisitor(SqlContext sqlContext) + : base(sqlContext.SqlSyntax) + { + _pd = sqlContext.PocoDataFactory.ForType(typeof(T)); + } + + protected override string VisitMemberAccess(MemberExpression m) + { + if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(T)) + { + //don't execute if compiled + if (Visited == false) + return GetFieldName(_pd, m.Member.Name); + + //already compiled, return + return string.Empty; + } + + if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) + { + //don't execute if compiled + if (Visited == false) + return GetFieldName(_pd, m.Member.Name); + + //already compiled, return + return string.Empty; + } + + var member = Expression.Convert(m, typeof(object)); + var lambda = Expression.Lambda>(member); + var getter = lambda.Compile(); + var o = getter(); + + SqlParameters.Add(o); + + //don't execute if compiled + if (Visited == false) + return $"@{SqlParameters.Count - 1}"; + + //already compiled, return + return string.Empty; + } + + protected virtual string GetFieldName(PocoData pocoData, string name) + { + var column = pocoData.Columns.FirstOrDefault(x => x.Value.MemberInfoData.Name == name); + var tableName = SqlSyntax.GetQuotedTableName(pocoData.TableInfo.TableName); + var columnName = SqlSyntax.GetQuotedColumnName(column.Value.ColumnName); + + return $"{tableName}.{columnName}"; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/Query.cs b/src/Umbraco.Core/Persistence/Querying/Query.cs index c69efe4a5f..868cca0a3e 100644 --- a/src/Umbraco.Core/Persistence/Querying/Query.cs +++ b/src/Umbraco.Core/Persistence/Querying/Query.cs @@ -1,9 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; @@ -34,7 +32,8 @@ namespace Umbraco.Core.Persistence.Querying { if (predicate != null) { - var expressionHelper = new ModelToSqlExpressionHelper(_sqlSyntax, _mappers); + //TODO: This should have an SqlSyntax object passed in, this ctor is relying on a singleton + var expressionHelper = new ModelToSqlExpressionVisitor(_sqlSyntax, _mappers); string whereExpression = expressionHelper.Visit(predicate); _wheres.Add(new Tuple(whereExpression, expressionHelper.GetSqlParameters())); @@ -46,7 +45,7 @@ namespace Umbraco.Core.Persistence.Querying { if (fieldSelector != null) { - var expressionHelper = new ModelToSqlExpressionHelper(_sqlSyntax, _mappers); + var expressionHelper = new ModelToSqlExpressionVisitor(_sqlSyntax, _mappers); string whereExpression = expressionHelper.Visit(fieldSelector); _wheres.Add(new Tuple(whereExpression + " IN (@values)", new object[] { new { @values = values } })); @@ -62,6 +61,5 @@ namespace Umbraco.Core.Persistence.Querying { return _wheres; } - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/QueryExtensions.cs b/src/Umbraco.Core/Persistence/Querying/QueryExtensions.cs new file mode 100644 index 0000000000..20c3409a40 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Querying/QueryExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Persistence.Querying +{ + /// + /// SD: This is a horrible hack but unless we break compatibility with anyone who's actually implemented IQuery{T} there's not much we can do. + /// The IQuery{T} interface is useless without having a GetWhereClauses method and cannot be used for tests. + /// We have to wait till v8 to make this change I suppose. + /// + internal static class QueryExtensions + { + /// + /// Returns all translated where clauses and their sql parameters + /// + /// + public static IEnumerable> GetWhereClauses(this IQuery query) + { + var q = query as Query; + if (q == null) + { + throw new NotSupportedException(typeof(IQuery) + " cannot be cast to " + typeof(Query)); + } + return q.GetWhereClauses(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs index 074374ae18..7c7ba29d6c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs @@ -8,17 +8,15 @@ using Umbraco.Core.DI; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { internal class AuditRepository : NPocoRepositoryBase, IAuditRepository { - public AuditRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public AuditRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index f472520f31..3dfabe99a2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -4,12 +4,12 @@ using System.Globalization; using System.Linq; using System.Xml; using NPoco; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; - using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; @@ -32,8 +32,8 @@ namespace Umbraco.Core.Persistence.Repositories private readonly CacheHelper _cacheHelper; private PermissionRepository _permissionRepository; - public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection, IMapperCollection mappers) - : base(work, cacheHelper, logger, contentSection, mappers) + public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection, IQueryFactory queryFactory) + : base(work, cacheHelper, logger, contentSection, queryFactory) { if (contentTypeRepository == null) throw new ArgumentNullException(nameof(contentTypeRepository)); if (templateRepository == null) throw new ArgumentNullException(nameof(templateRepository)); @@ -43,6 +43,8 @@ namespace Umbraco.Core.Persistence.Repositories _tagRepository = tagRepository; _cacheHelper = cacheHelper; + _publishedQuery = queryFactory.Create().Where(x => x.Published); + EnsureUniqueNaming = true; } @@ -109,6 +111,12 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + #region Static Queries + + private readonly IQuery _publishedQuery; + + #endregion + #region Overrides of NPocoRepositoryBase protected override Sql GetBaseQuery(bool isCount) @@ -587,18 +595,7 @@ namespace Umbraco.Core.Persistence.Repositories .OrderBy(x => x.Level) .OrderBy(x => x.SortOrder); - //NOTE: This doesn't allow properties to be part of the query - var dtos = Database.Fetch(sql); - - foreach (var dto in dtos) - { - // check cache first, if it exists and is published, use it - // it may exist and not be published as the cache has 'latest version used' - var fromCache = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); - yield return fromCache != null && fromCache.Published - ? fromCache - : CreateContentFromDto(dto, dto.VersionId); - } + return MapQueryDtos(Database.Fetch(sql), true); } public int CountPublished(string contentTypeAlias = null) @@ -700,7 +697,7 @@ namespace Umbraco.Core.Persistence.Repositories } return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - MapQueryDtos, + x => MapQueryDtos(x), orderBy, orderDirection, orderBySystemField, "cmsDocument", filterSql); } @@ -762,80 +759,76 @@ WHERE (@path LIKE {5})", return base.GetDatabaseFieldNameForOrderBy(orderBy); } - private IEnumerable MapQueryDtos(List dtos) + private IEnumerable MapQueryDtos(List dtos, bool withCache = false) { //nothing found - if (dtos.Any() == false) return Enumerable.Empty(); + var content = new IContent[dtos.Count]; + var defs = new List(); + var templateIds = new List(); - //content types - //NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types - var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray()) - .ToArray(); - - - var ids = dtos - .Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0) - .Select(x => x.TemplateId.Value).ToArray(); - - //NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types - var templates = ids.Length == 0 ? Enumerable.Empty() : _templateRepository.GetAll(ids).ToArray(); - - var dtosWithContentTypes = dtos - //This select into and null check are required because we don't have a foreign damn key on the contentType column - // http://issues.umbraco.org/issue/U4-5503 - .Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) }) - .Where(x => x.contentType != null) - .ToArray(); - - //Go get the property data for each document - var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition( - d.dto.NodeId, - d.dto.VersionId, - d.dto.ContentVersionDto.VersionDate, - d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, - d.contentType)); - - var propertyData = GetPropertyCollection(docDefs.ToArray()); - - return dtosWithContentTypes.Select(d => CreateContentFromDto( - d.dto, - contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId), - templates.FirstOrDefault(tem => tem.Id == (d.dto.TemplateId ?? -1)), - propertyData[d.dto.NodeId])); - } - - /// - /// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion. - /// - /// - /// - /// - /// - /// - private IContent CreateContentFromDto(DocumentDto dto, - IContentType contentType, - ITemplate template, - PropertyCollection propCollection) - { - var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); - var content = factory.BuildEntity(dto); - - //Check if template id is set on DocumentDto, and get ITemplate if it is. - if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) + for (var i = 0; i < dtos.Count; i++) { - content.Template = template ?? _templateRepository.Get(dto.TemplateId.Value); - } - else - { - //ensure there isn't one set. - content.Template = null; + var dto = dtos[i]; + + // if the cache contains the published version, use it + if (withCache) + { + var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + if (cached != null && cached.Published) + { + content[i] = cached; + continue; + } + } + + // else, need to fetch from the database + // content type repository is full-cache so OK to get each one independently + var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); + var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); + content[i] = factory.BuildEntity(dto); + + // need template + if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) + templateIds.Add(dto.TemplateId.Value); + + // need properties + defs.Add(new DocumentDefinition( + dto.NodeId, + dto.VersionId, + dto.ContentVersionDto.VersionDate, + dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, + contentType + )); } - content.Properties = propCollection; + // load all required templates in 1 query + var templates = _templateRepository.GetAll(templateIds.ToArray()) + .ToDictionary(x => x.Id, x => x); + + // load all properties for all documents from database in 1 query + var propertyData = GetPropertyCollection(defs.ToArray()); + + // assign + var dtoIndex = 0; + foreach (var def in defs) + { + // move to corresponding item (which has to exist) + while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++; + + // complete the item + var cc = content[dtoIndex]; + var dto = dtos[dtoIndex]; + ITemplate template = null; + if (dto.TemplateId.HasValue) + templates.TryGetValue(dto.TemplateId.Value, out template); // else null + cc.Template = template; + cc.Properties = propertyData[cc.Id]; + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((Entity) cc).ResetDirtyProperties(false); + } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((Entity)content).ResetDirtyProperties(false); return content; } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index 0e1ddc5490..c8af4bcaa0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ITemplateRepository _templateRepository; private IRepositoryCachePolicy _cachePolicy; - public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ITemplateRepository templateRepository, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ITemplateRepository templateRepository, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { _templateRepository = templateRepository; } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs index b97a7831d7..62d406f8f9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs @@ -31,10 +31,9 @@ namespace Umbraco.Core.Persistence.Repositories internal abstract class ContentTypeRepositoryBase : NPocoRepositoryBase, IReadRepository where TEntity : class, IContentTypeComposition { - protected ContentTypeRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) - { - } + protected ContentTypeRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) + { } public IEnumerable> Move(TEntity moving, EntityContainer container) { diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index 4c581a1729..d650f9b63d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -31,12 +31,11 @@ namespace Umbraco.Core.Persistence.Repositories private readonly IContentTypeRepository _contentTypeRepository; private readonly DataTypePreValueRepository _preValRepository; - public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, - IContentTypeRepository contentTypeRepository, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentTypeRepository contentTypeRepository, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { _contentTypeRepository = contentTypeRepository; - _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, mappers); + _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, queryFactory); } #region Overrides of RepositoryBase @@ -534,8 +533,8 @@ AND umbracoNode.id <> @id", /// private class DataTypePreValueRepository : NPocoRepositoryBase { - public DataTypePreValueRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public DataTypePreValueRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 95574fe05e..dc2c0559e3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -21,13 +21,10 @@ namespace Umbraco.Core.Persistence.Repositories internal class DictionaryRepository : NPocoRepositoryBase, IDictionaryRepository { private IRepositoryCachePolicy _cachePolicy; - private readonly IMapperCollection _mappers; - public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) - { - _mappers = mappers; - } + public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) + { } protected override IRepositoryCachePolicy CachePolicy { @@ -243,13 +240,13 @@ namespace Umbraco.Core.Persistence.Repositories public IDictionaryItem Get(Guid uniqueId) { - var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, RepositoryCache, Logger, _mappers); + var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, RepositoryCache, Logger, QueryFactory); return uniqueIdRepo.Get(uniqueId); } public IDictionaryItem Get(string key) { - var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, RepositoryCache, Logger, _mappers); + var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, RepositoryCache, Logger, QueryFactory); return keyRepo.Get(key); } @@ -297,8 +294,8 @@ namespace Umbraco.Core.Persistence.Repositories private IRepositoryCachePolicy _cachePolicy; private readonly DictionaryRepository _dictionaryRepository; - public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { _dictionaryRepository = dictionaryRepository; } @@ -358,8 +355,8 @@ namespace Umbraco.Core.Persistence.Repositories private IRepositoryCachePolicy _cachePolicy; private readonly DictionaryRepository _dictionaryRepository; - public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { _dictionaryRepository = dictionaryRepository; } diff --git a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs index a5231b8ea6..0acbbd1262 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs @@ -6,12 +6,8 @@ using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence.FaultHandling; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -22,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories { private IRepositoryCachePolicy _cachePolicy; - public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index 96a1999f4d..0944ec811f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly Guid _containerObjectType; - public EntityContainerRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers, Guid containerObjectType) - : base(work, cache, logger, mappers) + public EntityContainerRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory, Guid containerObjectType) + : base(work, cache, logger, queryFactory) { var allowedContainers = new[] {Constants.ObjectTypes.DocumentTypeContainerGuid, Constants.ObjectTypes.MediaTypeContainerGuid, Constants.ObjectTypes.DataTypeContainerGuid}; _containerObjectType = containerObjectType; diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index c2f1c503eb..4977363510 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -6,9 +6,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -21,12 +19,12 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class EntityRepository : DisposableObject, IEntityRepository { - private readonly QueryFactory _queryFactory; + private readonly IQueryFactory _queryFactory; - public EntityRepository(IDatabaseUnitOfWork work, IMapperCollection mappers) + public EntityRepository(IDatabaseUnitOfWork work, IQueryFactory queryFactory) { UnitOfWork = work; - _queryFactory = new QueryFactory(work.Database.SqlSyntax, mappers); + _queryFactory = queryFactory; } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs index 2d43ee5d9f..4871c8c84f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs @@ -9,17 +9,15 @@ using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { internal class ExternalLoginRepository : NPocoRepositoryBase, IExternalLoginRepository { - public ExternalLoginRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public ExternalLoginRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/DataTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/DataTypeContainerRepository.cs index 0be6f8fde9..006e99d83d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/DataTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/DataTypeContainerRepository.cs @@ -1,14 +1,14 @@ using Umbraco.Core.Cache; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { class DataTypeContainerRepository : EntityContainerRepository, IDataTypeContainerRepository { - public DataTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(uow, cache, logger, mappers, Constants.ObjectTypes.DataTypeContainerGuid) + public DataTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(uow, cache, logger, queryFactory, Constants.ObjectTypes.DataTypeContainerGuid) { } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/DocumentTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/DocumentTypeContainerRepository.cs index 8eaa210a97..516ecbd815 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/DocumentTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/DocumentTypeContainerRepository.cs @@ -1,14 +1,15 @@ using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { class DocumentTypeContainerRepository : EntityContainerRepository, IDocumentTypeContainerRepository { - public DocumentTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(uow, cache, logger, mappers, Constants.ObjectTypes.DocumentTypeContainerGuid) + public DocumentTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(uow, cache, logger, queryFactory, Constants.ObjectTypes.DocumentTypeContainerGuid) { } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/MediaTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/MediaTypeContainerRepository.cs index b11d241040..5a7947a6e5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/MediaTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/MediaTypeContainerRepository.cs @@ -1,14 +1,15 @@ using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { class MediaTypeContainerRepository : EntityContainerRepository, IMediaTypeContainerRepository { - public MediaTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(uow, cache, logger, mappers, Constants.ObjectTypes.MediaTypeContainerGuid) + public MediaTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(uow, cache, logger, queryFactory, Constants.ObjectTypes.MediaTypeContainerGuid) { } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs index b97576b5a6..00eca6635b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs @@ -9,9 +9,7 @@ using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -23,8 +21,8 @@ namespace Umbraco.Core.Persistence.Repositories { private IRepositoryCachePolicy _cachePolicy; - public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs index d49ab92efd..8250388ff9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs @@ -9,9 +9,7 @@ using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -19,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories internal class MacroRepository : NPocoRepositoryBase, IMacroRepository { - public MacroRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public MacroRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 75794912ac..c8c325480e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; -using System.Xml.Linq; using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; @@ -11,12 +10,9 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; - using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -29,8 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories private readonly IMediaTypeRepository _mediaTypeRepository; private readonly ITagRepository _tagRepository; - public MediaRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, IMapperCollection mappers) - : base(work, cache, logger, contentSection, mappers) + public MediaRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, IQueryFactory queryFactory) + : base(work, cache, logger, contentSection, queryFactory) { if (mediaTypeRepository == null) throw new ArgumentNullException(nameof(mediaTypeRepository)); if (tagRepository == null) throw new ArgumentNullException(nameof(tagRepository)); @@ -76,7 +72,7 @@ namespace Umbraco.Core.Persistence.Repositories sql.Where("umbracoNode.id in (@ids)", new { /*ids =*/ ids }); } - return ProcessQuery(sql); + return MapQueryDtos(Database.Fetch(sql)); } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -86,7 +82,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = translator.Translate() .OrderBy(x => x.SortOrder); - return ProcessQuery(sql); + return MapQueryDtos(Database.Fetch(sql)); } #endregion @@ -144,6 +140,15 @@ namespace Umbraco.Core.Persistence.Repositories #region Overrides of VersionableRepositoryBase + public override IEnumerable GetAllVersions(int id) + { + var sql = GetBaseQuery(false) + .Where(GetBaseWhereClause(), new { Id = id }) + .OrderByDescending(x => x.VersionDate); + + return MapQueryDtos(Database.Fetch(sql), true); + } + public override IMedia GetByVersion(Guid versionId) { var sql = GetBaseQuery(false); @@ -421,46 +426,66 @@ namespace Umbraco.Core.Persistence.Repositories } return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - MapQueryDtos, orderBy, orderDirection, orderBySystemField, "cmsContentVersion", + x => MapQueryDtos(x), orderBy, orderDirection, orderBySystemField, "cmsContentVersion", filterSql); } - private IEnumerable ProcessQuery(Sql sql) + private IEnumerable MapQueryDtos(List dtos, bool withCache = false) { - //NOTE: This doesn't allow properties to be part of the query - var dtos = Database.Fetch(sql); - return MapQueryDtos(dtos); - } + var content = new IMedia[dtos.Count]; + var defs = new List(); - private IEnumerable MapQueryDtos(List dtos) - { - var ids = dtos.Select(x => x.ContentDto.ContentTypeId).ToArray(); + for (var i = 0; i < dtos.Count; i++) + { + var dto = dtos[i]; - //content types - var contentTypes = ids.Length == 0 ? Enumerable.Empty() : _mediaTypeRepository.GetAll(ids).ToArray(); + // if the cache contains the item, use it + if (withCache) + { + var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + if (cached != null) + { + content[i] = cached; + continue; + } + } - var dtosWithContentTypes = dtos - //This select into and null check are required because we don't have a foreign damn key on the contentType column - // http://issues.umbraco.org/issue/U4-5503 - .Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentDto.ContentTypeId) }) - .Where(x => x.contentType != null) - .ToArray(); + // else, need to fetch from the database + // content type repository is full-cache so OK to get each one independently + var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); + var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId); + content[i] = factory.BuildEntity(dto); - //Go get the property data for each document - var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition( - d.dto.NodeId, - d.dto.VersionId, - d.dto.VersionDate, - d.dto.ContentDto.NodeDto.CreateDate, - d.contentType)) - .ToArray(); + // need properties + defs.Add(new DocumentDefinition( + dto.NodeId, + dto.VersionId, + dto.VersionDate, + dto.ContentDto.NodeDto.CreateDate, + contentType + )); + } - var propertyData = GetPropertyCollection(docDefs); + // load all properties for all documents from database in 1 query + var propertyData = GetPropertyCollection(defs.ToArray()); - return dtosWithContentTypes.Select(d => CreateMediaFromDto( - d.dto, - contentTypes.First(ct => ct.Id == d.dto.ContentDto.ContentTypeId), - propertyData[d.dto.NodeId])); + // assign + var dtoIndex = 0; + foreach (var def in defs) + { + // move to corresponding item (which has to exist) + while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++; + + // complete the item + var cc = content[dtoIndex]; + cc.Properties = propertyData[cc.Id]; + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((Entity)cc).ResetDirtyProperties(false); + } + + return content; } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index b07ea0c5bc..1980d5110f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -6,7 +6,6 @@ using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; @@ -19,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories { private IRepositoryCachePolicy _cachePolicy; - public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } protected override IRepositoryCachePolicy CachePolicy diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index b2a361dc10..367166f177 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -10,7 +10,6 @@ using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Cache; -using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Persistence.Repositories { @@ -18,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories internal class MemberGroupRepository : NPocoRepositoryBase, IMemberGroupRepository { - public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } private readonly MemberGroupFactory _modelFactory = new MemberGroupFactory(); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 9d0517bda2..9654906de9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; - +using Umbraco.Core.Cache; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; @@ -30,8 +30,8 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ITagRepository _tagRepository; private readonly IMemberGroupRepository _memberGroupRepository; - public MemberRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, IContentSection contentSection, IMapperCollection mappers) - : base(work, cache, logger, contentSection, mappers) + public MemberRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, IContentSection contentSection, IQueryFactory queryFactory) + : base(work, cache, logger, contentSection, queryFactory) { if (memberTypeRepository == null) throw new ArgumentNullException(nameof(memberTypeRepository)); if (tagRepository == null) throw new ArgumentNullException(nameof(tagRepository)); @@ -564,7 +564,7 @@ namespace Umbraco.Core.Persistence.Repositories // better to create the query text only once! return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - MapQueryDtos, orderBy, orderDirection, orderBySystemField, "cmsMember", + x => MapQueryDtos(x), orderBy, orderDirection, orderBySystemField, "cmsMember", filterSql); } @@ -598,56 +598,62 @@ namespace Umbraco.Core.Persistence.Repositories return base.GetDatabaseFieldNameForOrderBy(orderBy); } - private IEnumerable MapQueryDtos(List dtos) + private IEnumerable MapQueryDtos(List dtos, bool withCache = false) { - var ids = dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray(); + var content = new IMember[dtos.Count]; + var defs = new List(); - //content types - var contentTypes = ids.Length == 0 ? Enumerable.Empty() : _memberTypeRepository.GetAll(ids).ToArray(); + for (var i = 0; i < dtos.Count; i++) + { + var dto = dtos[i]; - var dtosWithContentTypes = dtos - //This select into and null check are required because we don't have a foreign damn key on the contentType column - // http://issues.umbraco.org/issue/U4-5503 - .Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) }) - .Where(x => x.contentType != null) - .ToArray(); + // if the cache contains the item, use it + if (withCache) + { + var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + if (cached != null) + { + content[i] = cached; + continue; + } + } - //Go get the property data for each document - IEnumerable docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition( - d.dto.NodeId, - d.dto.ContentVersionDto.VersionId, - d.dto.ContentVersionDto.VersionDate, - d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, - d.contentType)); + // else, need to fetch from the database + // content type repository is full-cache so OK to get each one independently + var contentType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); + var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.NodeId); + content[i] = factory.BuildEntity(dto); - var propertyData = GetPropertyCollection(docDefs.ToArray()); + // need properties + defs.Add(new DocumentDefinition( + dto.NodeId, + dto.ContentVersionDto.VersionId, + dto.ContentVersionDto.VersionDate, + dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, + contentType + )); + } - return dtosWithContentTypes.Select(d => CreateMemberFromDto( - d.dto, - contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId), - propertyData[d.dto.NodeId])); - } + // load all properties for all documents from database in 1 query + var propertyData = GetPropertyCollection(defs.ToArray()); - /// - /// Private method to create a member object from a MemberDto - /// - /// - /// - /// - /// - private IMember CreateMemberFromDto(MemberDto dto, - IMemberType contentType, - PropertyCollection propCollection) - { - var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.ContentVersionDto.NodeId); - var member = factory.BuildEntity(dto); + // assign + var dtoIndex = 0; + foreach (var def in defs) + { + // move to corresponding item (which has to exist) + while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++; - member.Properties = propCollection; + // complete the item + var cc = content[dtoIndex]; + cc.Properties = propertyData[cc.Id]; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((Entity)member).ResetDirtyProperties(false); - return member; + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((Entity)cc).ResetDirtyProperties(false); + } + + return content; } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 983fc6ca5a..1cdc36807d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -21,8 +21,8 @@ namespace Umbraco.Core.Persistence.Repositories { private IRepositoryCachePolicy _cachePolicy; - public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } protected override IRepositoryCachePolicy CachePolicy diff --git a/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs index d47f579ea4..376e620eeb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs @@ -10,17 +10,15 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { internal class MigrationEntryRepository : NPocoRepositoryBase, IMigrationEntryRepository { - public MigrationEntryRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public MigrationEntryRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs index 80a85c8b00..9c9843ba1e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs @@ -4,7 +4,6 @@ using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; @@ -25,11 +24,11 @@ namespace Umbraco.Core.Persistence.Repositories /// A database unit of work. /// A cache helper. /// A logger. - /// A mappers collection. - protected NPocoRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) + /// A query factory. + protected NPocoRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) : base(work, cache, logger) { - QueryFactory = new QueryFactory(SqlSyntax, mappers); + QueryFactory = queryFactory; } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs index 2b0bbb25a5..03eaf6b839 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs @@ -7,9 +7,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -18,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories { private IRepositoryCachePolicy _cachePolicy; - public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } protected override IRepositoryCachePolicy CachePolicy diff --git a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs index 7dc04f2681..a0720320ee 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -12,8 +13,8 @@ namespace Umbraco.Core.Persistence.Repositories where TEntity : class, IUmbracoEntity where TRepository : class, IRepository { - protected RecycleBinRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection, IMapperCollection mappers) - : base(work, cache, logger, contentSection, mappers) + protected RecycleBinRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection, IQueryFactory queryFactory) + : base(work, cache, logger, contentSection, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs index 0a5f8c0237..dc1345d845 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs @@ -1,14 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography; -using System.Text; using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; @@ -16,8 +13,8 @@ namespace Umbraco.Core.Persistence.Repositories { internal class RedirectUrlRepository : NPocoRepositoryBase, IRedirectUrlRepository { - public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } protected override int PerformCount(IQuery query) diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs index ee4a4689f7..e31d2d31b8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs @@ -11,9 +11,7 @@ using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -25,8 +23,8 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly IRelationTypeRepository _relationTypeRepository; - public RelationRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IRelationTypeRepository relationTypeRepository, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public RelationRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IRelationTypeRepository relationTypeRepository, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { _relationTypeRepository = relationTypeRepository; } @@ -47,34 +45,17 @@ namespace Umbraco.Core.Persistence.Repositories throw new Exception(string.Format("RelationType with Id: {0} doesn't exist", dto.RelationType)); var factory = new RelationFactory(relationType); - var entity = factory.BuildEntity(dto); - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); - - return entity; + return DtoToEntity(dto, factory); } - //TODO: Fix N+1 ! - protected override IEnumerable PerformGetAll(params int[] ids) { - if (ids.Any()) - { - foreach (var id in ids) - { - yield return Get(id); - } - } - else - { - var dtos = Database.Fetch("WHERE id > 0"); - foreach (var dto in dtos) - { - yield return Get(dto.Id); - } - } + var sql = GetBaseQuery(false); + if (ids.Length > 0) + sql.WhereIn(x => x.Id, ids); + sql.OrderBy(x => x.RelationType); + var dtos = Database.Fetch(sql); + return DtosToEntities(dtos); } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -82,13 +63,36 @@ namespace Umbraco.Core.Persistence.Repositories var sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); - + sql.OrderBy(x => x.RelationType); var dtos = Database.Fetch(sql); + return DtosToEntities(dtos); + } - foreach (var dto in dtos) + private IEnumerable DtosToEntities(IEnumerable dtos) + { + // in most cases, the relation type will be the same for all of them, + // plus we've ordered the relations by type, so try to allocate as few + // factories as possible - bearing in mind that relation types are cached + RelationFactory factory = null; + var relationTypeId = -1; + + return dtos.Select(x => { - yield return Get(dto.Id); - } + if (relationTypeId != x.RelationType) + factory = new RelationFactory(_relationTypeRepository.Get(relationTypeId = x.RelationType)); + return DtoToEntity(x, factory); + }); + } + + private static IRelation DtoToEntity(RelationDto dto, RelationFactory factory) + { + var entity = factory.BuildEntity(dto); + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); + + return entity; } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs index a609b8c023..c8299cd118 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs @@ -1,19 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using NPoco; using Umbraco.Core.Cache; -using Umbraco.Core.DI; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; - using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -23,50 +18,45 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class RelationTypeRepository : NPocoRepositoryBase, IRelationTypeRepository { + private IRepositoryCachePolicy _cachePolicy; - public RelationTypeRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public RelationTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) + { } + + protected override IRepositoryCachePolicy CachePolicy { + get + { + if (_cachePolicy != null) return _cachePolicy; + + // assuming we don't have tons of relation types, use a FullDataSet policy, ie + // cache the entire GetAll result once in a single collection - which can expire + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true); + + return _cachePolicy; + } } #region Overrides of RepositoryBase protected override IRelationType PerformGet(int id) { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); - - var dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); - if (dto == null) - return null; - - var factory = new RelationTypeFactory(); - var entity = factory.BuildEntity(dto); - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); - - return entity; + // use the underlying GetAll which will force cache all content types + return GetAll().FirstOrDefault(x => x.Id == id); } protected override IEnumerable PerformGetAll(params int[] ids) { + var sql = GetBaseQuery(false); + + // should not happen due to the cache policy if (ids.Any()) - { - foreach (var id in ids) - { - yield return Get(id); - } - } - else - { - var dtos = Database.Fetch("WHERE id > 0"); - foreach (var dto in dtos) - { - yield return Get(dto.Id); - } - } + throw new NotImplementedException(); + + var dtos = Database.Fetch(sql); + var factory = new RelationTypeFactory(); + return dtos.Select(x => DtoToEntity(x, factory)); } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -76,11 +66,19 @@ namespace Umbraco.Core.Persistence.Repositories var sql = translator.Translate(); var dtos = Database.Fetch(sql); + var factory = new RelationTypeFactory(); + return dtos.Select(x => DtoToEntity(x, factory)); + } - foreach (var dto in dtos) - { - yield return Get(dto.Id); - } + private static IRelationType DtoToEntity(RelationTypeDto dto, RelationTypeFactory factory) + { + var entity = factory.BuildEntity(dto); + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((TracksChangesEntityBase) entity).ResetDirtyProperties(false); + + return entity; } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index de8ee024c3..ea3dfb46ec 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -71,6 +71,12 @@ namespace Umbraco.Core.Persistence.Repositories /// public abstract IQueryFactory QueryFactory { get; } + #region Static Queries + + private IQuery _hasIdQuery; + + #endregion + protected virtual TId GetEntityId(TEntity entity) { return (TId)(object)entity.Id; @@ -91,9 +97,14 @@ namespace Umbraco.Core.Persistence.Repositories var options = new RepositoryCachePolicyOptions(() => { + //create it once if it is needed (no need for locking here) + if (_hasIdQuery == null) + { + _hasIdQuery = Query.Where(x => x.Id != 0); + } + //Get count of all entities of current type (TEntity) to ensure cached result is correct - var query = Query.Where(x => x.Id != 0); - return PerformCount(query); + return PerformCount(_hasIdQuery); }); _cachePolicy = new DefaultRepositoryCachePolicy(RuntimeCache, options); diff --git a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs index 4132d9de6a..f0b913a41f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs @@ -5,10 +5,8 @@ using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; @@ -18,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories { private IRepositoryCachePolicy _cachePolicy; - public ServerRegistrationRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IMapperCollection mappers) - : base(work, CacheHelper.CreateDisabledCacheHelper(), logger, mappers) + public ServerRegistrationRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IQueryFactory queryFactory) + : base(work, CacheHelper.CreateDisabledCacheHelper(), logger, queryFactory) { } protected override IRepositoryCachePolicy CachePolicy => _cachePolicy diff --git a/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs index 9c471a98e0..9b63a3b4f6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs @@ -5,9 +5,7 @@ using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -20,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories where TDto: class { - protected SimpleGetRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + protected SimpleGetRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs index 77224a0eaa..0b92ccf0b9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs @@ -9,17 +9,15 @@ using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { internal class TagRepository : NPocoRepositoryBase, ITagRepository { - public TagRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public TagRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs index 0585540f09..e5304376a8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs @@ -1,27 +1,23 @@ using System; using System.Collections.Generic; using System.Linq; -using AutoMapper; using LightInject; using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.DI; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { internal class TaskRepository : NPocoRepositoryBase, ITaskRepository { - public TaskRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public TaskRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs index 442daed787..ee739b0125 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs @@ -9,17 +9,15 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { internal class TaskTypeRepository : NPocoRepositoryBase, ITaskTypeRepository { - public TaskTypeRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public TaskTypeRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 4feff6db71..58321fb4b1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -37,8 +37,8 @@ namespace Umbraco.Core.Persistence.Repositories private readonly MasterPageHelper _masterPageHelper; private IRepositoryCachePolicy _cachePolicy; - public TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { _masterpagesFileSystem = masterpageFileSystem; _viewsFileSystem = viewFileSystem; diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 5103da91f0..62a40569f9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -28,8 +28,8 @@ namespace Umbraco.Core.Persistence.Repositories private readonly CacheHelper _cacheHelper; private PermissionRepository _permissionRepository; - public UserRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IUserTypeRepository userTypeRepository, IMapperCollection mappers) - : base(work, cacheHelper, logger, mappers) + public UserRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IUserTypeRepository userTypeRepository, IQueryFactory queryFactory) + : base(work, cacheHelper, logger, queryFactory) { _userTypeRepository = userTypeRepository; _cacheHelper = cacheHelper; diff --git a/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs index c1aabb4fe3..0e7f1d8208 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs @@ -20,8 +20,8 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class UserTypeRepository : NPocoRepositoryBase, IUserTypeRepository { - public UserTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMapperCollection mappers) - : base(work, cache, logger, mappers) + public UserTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 3213ce9745..a34a2389b4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -50,8 +50,8 @@ namespace Umbraco.Core.Persistence.Repositories { //private readonly IContentSection _contentSection; - protected VersionableRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection, IMapperCollection mappers) - : base(work, cache, logger, mappers) + protected VersionableRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection, IQueryFactory queryFactory) + : base(work, cache, logger, queryFactory) { //_contentSection = contentSection; } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 51f19fe57d..7672860f2c 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Persistence.SqlSyntax { /// - /// Represents an SqlSyntaxProvider for Sql Server + /// Represents an SqlSyntaxProvider for Sql Server. /// [SqlSyntaxProvider(Constants.DbProviderNames.SqlServer)] public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase @@ -38,6 +38,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax V2008 = 4, V2012 = 5, V2014 = 6, + V2016 = 7, Other = 99 } @@ -70,6 +71,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax case "??": ProductVersionName = VersionName.Invalid; break; + case "13": + ProductVersionName = VersionName.V2016; + break; case "12": ProductVersionName = VersionName.V2014; break; @@ -136,7 +140,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax { var items = db.Fetch("SELECT TableName = t.Name,ColumnName = c.Name,dc.Name,dc.[Definition] FROM sys.tables t INNER JOIN sys.default_constraints dc ON t.object_id = dc.parent_object_id INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id AND c.column_id = dc.parent_column_id"); return items.Select(x => new Tuple(x.TableName, x.ColumnName, x.Name, x.Definition)); - } + } public override IEnumerable GetTablesInSchema(Database db) { @@ -181,9 +185,9 @@ from sys.tables as T inner join sys.indexes as I on T.[object_id] = I.[object_id inner join sys.all_columns as AC on IC.[object_id] = AC.[object_id] and IC.[column_id] = AC.[column_id] WHERE I.name NOT LIKE 'PK_%' order by T.name, I.name"); - return items.Select(item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, + return items.Select(item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, item.UNIQUE == 1)).ToList(); - + } public override bool DoesTableExist(Database db, string tableName) @@ -225,7 +229,7 @@ order by T.name, I.name"); switch (systemMethod) { case SystemMethods.NewGuid: - return "NEWID()"; + return "NEWID()"; case SystemMethods.CurrentDateTime: return "GETDATE()"; //case SystemMethods.NewSequentialId: @@ -239,7 +243,6 @@ order by T.name, I.name"); public override string DeleteDefaultConstraint => "ALTER TABLE [{0}] DROP CONSTRAINT [DF_{0}_{1}]"; - public override string DropIndex => "DROP INDEX {0} ON {1}"; public override string RenameColumn => "sp_rename '{0}.{1}', '{2}', 'COLUMN'"; diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index ef52635030..4c21b643c3 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -28,14 +28,22 @@ namespace Umbraco.Core.Services IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, + IQueryFactory queryFactory, MediaFileSystem mediaFileSystem) : base(provider, logger, eventMessagesFactory) { _mediaFileSystem = mediaFileSystem; + _notTrashedQuery = queryFactory.Create().Where(x => x.Trashed == false); } #endregion + #region Static Queries + + private readonly IQuery _notTrashedQuery; + + #endregion + #region Count public int CountPublished(string contentTypeAlias = null) @@ -826,8 +834,7 @@ namespace Umbraco.Core.Services { uow.ReadLock(Constants.Locks.ContentTree); var repository = uow.CreateRepository(); - var query = repository.Query.Where(x => x.Trashed == false); - var content = repository.GetByPublishedVersion(query); + var content = repository.GetByPublishedVersion(_notTrashedQuery); uow.Complete(); return content; } diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index ca8e936c3a..5acf19bc21 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -20,17 +20,18 @@ namespace Umbraco.Core.Services { private readonly IRuntimeCacheProvider _runtimeCache; private readonly Dictionary>> _supportedObjectTypes; - public EntityService(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, - IContentService contentService, IContentTypeService contentTypeService, + IContentService contentService, IContentTypeService contentTypeService, IMediaService mediaService, IMediaTypeService mediaTypeService, IDataTypeService dataTypeService, IMemberService memberService, IMemberTypeService memberTypeService, + IQueryFactory queryFactory, IRuntimeCacheProvider runtimeCache) : base(provider, logger, eventMessagesFactory) { _runtimeCache = runtimeCache; + _rootEntityQuery = queryFactory.Create().Where(x => x.ParentId == -1); _supportedObjectTypes = new Dictionary>> { @@ -60,12 +61,18 @@ namespace Umbraco.Core.Services // ParentId = found.ParentId // }; // } - + //})} }; } + #region Static Queries + + private readonly IQuery _rootEntityQuery; + + #endregion + /// /// Returns the integer id for a given GUID /// @@ -108,7 +115,7 @@ namespace Umbraco.Core.Services } uow.Complete(); return id; - } + } }); return result.HasValue ? Attempt.Succeed(result.Value) : Attempt.Fail(); } @@ -435,8 +442,7 @@ namespace Umbraco.Core.Services using (var uow = UowProvider.CreateUnitOfWork()) { var repository = uow.CreateRepository(); - var query = repository.Query.Where(x => x.ParentId == -1); - var entities = repository.GetByQuery(query, objectTypeId); + var entities = repository.GetByQuery(_rootEntityQuery, objectTypeId); uow.Complete(); return entities; } diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index a3b6e6a58c..9478c0f4c8 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Xml.Linq; using Umbraco.Core.Configuration; using System.IO; using Umbraco.Core.Models; diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 8cb8345046..e4258cd9b0 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -181,7 +181,7 @@ namespace Umbraco.Core /// /// /// - internal static string CleanForXss(this string input, params char[] ignoreFromClean) + public static string CleanForXss(this string input, params char[] ignoreFromClean) { //remove any html input = input.StripHtml(); diff --git a/src/Umbraco.Core/Sync/SingleServerRegistrar.cs b/src/Umbraco.Core/Sync/SingleServerRegistrar.cs new file mode 100644 index 0000000000..dec0342fc0 --- /dev/null +++ b/src/Umbraco.Core/Sync/SingleServerRegistrar.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Sync +{ + public class SingleServerRegistrar : IServerRegistrar + { + private readonly IRuntimeState _runtime; + private readonly Lazy _registrations; + + public IEnumerable Registrations => _registrations.Value; + + public SingleServerRegistrar(IRuntimeState runtime) + { + _runtime = runtime; + _registrations = new Lazy(() => new[] { new ServerAddressImpl(_runtime.ApplicationUrl.ToString()) }); + } + + public ServerRole GetCurrentServerRole() + { + return ServerRole.Single; + } + + public string GetCurrentServerUmbracoApplicationUrl() + { + return _runtime.ApplicationUrl.ToString(); + } + + private class ServerAddressImpl : IServerAddress + { + public ServerAddressImpl(string serverAddress) + { + ServerAddress = serverAddress; + } + + public string ServerAddress { get; } + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index f7a7fb1b28..bb942ccef5 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -331,7 +331,6 @@ - @@ -349,7 +348,6 @@ - Component @@ -374,6 +372,7 @@ + @@ -386,7 +385,7 @@ - + @@ -398,6 +397,9 @@ + + + @@ -407,6 +409,7 @@ + @@ -442,6 +445,7 @@ + @@ -503,6 +507,7 @@ + @@ -987,10 +992,10 @@ - - + + - + @@ -1256,6 +1261,7 @@ + @@ -1365,7 +1371,7 @@ - -
-
- - -
-
- - -
-
- - -
-
-
-
-
- -
-
- -
-
-
- - -
- - - - - -
-

Welcome to The Friendly CMS

+
+ + + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+ +
+
+
+ + +
+
+ + + + +
+

Welcome to The Friendly CMS

Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible.

@@ -50,46 +50,46 @@
  • Watch our tutorial videos (some are free, some require a subscription)
  • Find out about our productivity boosting tools and commercial support
  • Find out about real-life training and certification opportunities
  • - - -
    -
    - - Umbraco.TV - Hours of Umbraco Video Tutorials - - - - -

    Umbraco.TV - Learn from the source!

    -
    - -

    - Umbraco.TV will help you go from zero to Umbraco - hero at a pace that suits you. Our easy to follow - online training videos will give you the fundamental - knowledge to start building awesome Umbraco websites. -

    -
    - -
    - - - Our Umbraco - - - -

    Our Umbraco - The Friendliest Community

    -
    - -

    - Our Umbraco - the official community site is your one - stop for everything Umbraco. Whether you need a - question answered or looking for cool plugins, the - worlds best community is just a click away. -

    - -
    -
    -
    - + + +
    +
    + + Umbraco.TV - Hours of Umbraco Video Tutorials + + + + +

    Umbraco.TV - Learn from the source!

    +
    + +

    + Umbraco.TV will help you go from zero to Umbraco + hero at a pace that suits you. Our easy to follow + online training videos will give you the fundamental + knowledge to start building awesome Umbraco websites. +

    +
    + +
    + + + Our Umbraco + + + +

    Our Umbraco - The Friendliest Community

    +
    + +

    + Our Umbraco - the official community site is your one + stop for everything Umbraco. Whether you need a + question answered or looking for cool plugins, the + worlds best community is just a click away. +

    + +
    +
    +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/media/move.html b/src/Umbraco.Web.UI.Client/src/views/media/move.html index 95c30dfc40..3f71340ee3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/move.html @@ -3,8 +3,10 @@

    - Choose where to move {{currentNode.name}} to in the tree structure below -

    + Choose where to move + {{currentNode.name}} + to in the tree structure below +

    {{error.errorMsg}}

    diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js index e5fba69a88..e34bc48ecd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js @@ -12,6 +12,7 @@ status: "", progress:0 }; + vm.installCompleted = false; vm.zipFile = { uploadStatus: "idle", uploadProgress: 0, @@ -137,10 +138,10 @@ localStorageService.set("packageInstallUri", "installed"); } - //reload on next digest (after cookie) - $timeout(function () { - $window.location.reload(true); - }); + vm.installState.status = localizationService.localize("packager_installStateCompleted"); + vm.installCompleted = true; + + }, installError); @@ -150,6 +151,13 @@ //This will return a rejection meaning that the promise change above will stop return $q.reject(); } + + vm.reloadPage = function() { + //reload on next digest (after cookie) + $timeout(function () { + $window.location.reload(true); + }); + } } angular.module("umbraco").controller("Umbraco.Editors.Packages.InstallLocalController", PackagesInstallLocalController); diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html index 499e844588..6bb7c6fb14 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html @@ -159,6 +159,19 @@

    {{vm.installState.status}}

    + + +
    + + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js index e4afb661e3..5ae1d4bf2c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js @@ -30,6 +30,7 @@ vm.openLightbox = openLightbox; vm.closeLightbox = closeLightbox; vm.search = search; + vm.installCompleted = false; var currSort = "Latest"; //used to cancel any request in progress if another one needs to take it's place @@ -215,10 +216,8 @@ localStorageService.set("packageInstallUri", result.postInstallationPath); } - //reload on next digest (after cookie) - $timeout(function() { - window.location.reload(true); - }); + vm.installState.status = localizationService.localize("packager_installStateCompleted"); + vm.installCompleted = true; }, error); @@ -277,6 +276,13 @@ searchDebounced(); } + vm.reloadPage = function () { + //reload on next digest (after cookie) + $timeout(function () { + window.location.reload(true); + }); + } + init(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html index 3975dc96d9..e7b14182ca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html @@ -340,6 +340,16 @@

    {{vm.installState.status}}

    +
    + + +
    + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html index 4360996b55..16c5efe799 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html @@ -39,7 +39,8 @@ on-drag-enter="vm.dragEnter()"> + + Web.Template.config @@ -1068,6 +1070,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v15.0 diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml index 446a82f510..f6b93139ce 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml @@ -64,21 +64,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "='" + property.Value.ToString() + "'"); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style='" + string.Join(" ", cssVals) + "'"); + if (cssVals.Any()) + attrs.Add("style='" + string.Join(" ", cssVals) + "'"); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml index 6bc730e1f8..c5fabe2abf 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml @@ -64,21 +64,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "=\"" + property.Value.ToString() + "\""); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style=\"" + string.Join(" ", cssVals) + "\""); + if (cssVals.Any()) + attrs.Add("style=\"" + string.Join(" ", cssVals) + "\""); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml index 1244821d7e..b7e8ef34fb 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml @@ -5,6 +5,7 @@ @* Razor helpers located at the bottom of this file *@ + @if (Model != null && Model.sections != null) { var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1; @@ -59,21 +60,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "='" + property.Value.ToString() + "'"); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style='" + string.Join(" ", cssVals) + "'"); + if (cssVals.Any()) + attrs.Add("style='" + string.Join(" ", cssVals) + "'"); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml index f76028d296..3a4fa3b8e2 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml @@ -64,21 +64,29 @@ JObject cfg = contentItem.config; if(cfg != null) - foreach (JProperty property in cfg.Properties()) { - attrs.Add(property.Name + "=\"" + property.Value.ToString() + "\""); + foreach (JProperty property in cfg.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + attrs.Add(property.Name + "=\"" + propertyValue + "\""); } - + JObject style = contentItem.styles; - if (style != null) { - var cssVals = new List(); - foreach (JProperty property in style.Properties()) - cssVals.Add(property.Name + ":" + property.Value.ToString() + ";"); + if (style != null) { + var cssVals = new List(); + foreach (JProperty property in style.Properties()) + { + var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString()); + if (string.IsNullOrWhiteSpace(propertyValue) == false) + { + cssVals.Add(property.Name + ":" + propertyValue + ";"); + } + } - if (cssVals.Any()) - attrs.Add("style=\"" + string.Join(" ", cssVals) + "\""); + if (cssVals.Any()) + attrs.Add("style=\"" + string.Join(" ", cssVals) + "\""); } - + return new MvcHtmlString(string.Join(" ", attrs)); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Base.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Base.cshtml index a86c04819a..ffb7603048 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Base.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Base.cshtml @@ -1,5 +1,4 @@ @model dynamic -@using Umbraco.Web.Templates @functions { public static string EditorView(dynamic contentItem) diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml index 4fd66ddb90..c27be6bcdf 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml @@ -1,3 +1,2 @@ @model dynamic -@using Umbraco.Web.Templates @Html.Raw(Model.value) diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Macro.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Macro.cshtml index e0822808d8..ed08bb2484 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Macro.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Macro.cshtml @@ -1,6 +1,4 @@ @inherits UmbracoViewPage -@using Umbraco.Web.Templates - @if (Model.value != null) { diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml index f5dfc6459c..5b5adbdc7d 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml @@ -1,5 +1,4 @@ @model dynamic -@using Umbraco.Web.Templates @if (Model.value != null) { diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml index a031c658a9..5a570efdb5 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml @@ -4,10 +4,9 @@ @if (Model.editor.config.markup != null) { string markup = Model.editor.config.markup.ToString(); - var UmbracoHelper = new UmbracoHelper(UmbracoContext.Current); - markup = markup.Replace("#value#", UmbracoHelper.ReplaceLineBreaksForHtml(Model.value.ToString())); + markup = markup.Replace("#value#", UmbracoHelper.ReplaceLineBreaksForHtml(TemplateUtilities.CleanForXss(Model.value.ToString()))); markup = markup.Replace("#style#", Model.editor.config.style.ToString()); diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 104c81be7e..a6b7766eac 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -30,6 +30,9 @@ Genindlæs elementer Genudgiv hele sitet Gendan + Sæt rettigheder for siden %0% + Hvor vil du flytte + hen til i træstrukturen? Rettigheder Fortryd ændringer Send til udgivelse @@ -202,12 +205,12 @@ Færdig - + Slettede %0% element Slettede %0% elementer Slettede %0% ud af %1% element Slettede %0% ud af %1% elementer - + Udgav %0% element Udgav %0% elementer Udgav %0% ud af %1% element @@ -274,17 +277,11 @@ Vælg Se cache element Opret mappe... - Relatér til original - + Inkludér undersider Link til side - - Åbner det linket dokument i et nyt vindue eller fane - Åbner det linket dokument i fuld visning af vinduet - Åbner det linket dokument i "parent frame" - + Åben linket i et nyt vindue eller fane Link til medie - Vælg medie Vælg ikon Vælg item @@ -293,20 +290,18 @@ Vælg indhold Vælg medlem Vælg medlemsgruppe - Der er ingen parametre for denne makro - Link dit - Fjern link fra dit - + Fjern link fra dit konto - Vælg editor - + Du tilføjer flere sprog under 'sprog' i menuen til venstre - ]]> + ]]> + Kulturnavn Rediger navnet på ordbogselementet. @@ -327,17 +322,17 @@ Indtast nøgleord (tryk på Enter efter hvert nøgleord)... - Tillad på rodniveau + Tillad på rodniveau Kun dokumenttyper med denne indstilling aktiveret oprettes i rodniveau under Inhold og Mediearkiv Tilladte typer - Sammensætning af dokumenttyper + Sammensætning af dokumenttyper Opret Slet fane Beskrivelse Ny fane Fane Thumbnail - Aktiver listevisning + Aktiver listevisning Viser undersider i en søgbar liste, undersider vises ikke i indholdstræet Nuværende listevisning Den aktive listevisningsdatatype @@ -372,7 +367,7 @@ Der skete en fejl på severen - Denne filttype er blevet deaktiveret af administratoren + Denne filttype er blevet deaktiveret af administratoren OBS! Selvom CodeMirror er slået til i konfigurationen, så er den deaktiveret i Internet Explorer fordi den ikke er stabil nok. Du skal udfylde både Alias & Navn på den nye egenskabstype! Der mangler læse/skrive rettigheder til bestemte filer og mapper @@ -388,7 +383,7 @@ Du kan ikke opdele en celle, som ikke allerede er delt. Fejl i XSLT kode Din XSLT er ikke opdateret, da det indeholdt en fejl - Der er et problem med den datatype, der bruges til denn egenskab. Kontroller konfigurationen og prøv igen. + Der er et problem med den datatype, der bruges til denn egenskab. Kontroller konfigurationen og prøv igen. Om @@ -474,7 +469,8 @@ Hvilken side skal vises efter at formularen er sendt Størrelse Sortér - Indsend + Indsend + Type Skriv for at søge... Op @@ -513,22 +509,22 @@ - Tilføj fane - Tilføj egenskab - Tilføj editor - Tilføj skabelon - Tilføj child node - Tilføj child + Tilføj fane + Tilføj egenskab + Tilføj editor + Tilføj skabelon + Tilføj child node + Tilføj child - Rediger datatype + Rediger datatype - Naviger sektioner + Naviger sektioner - Genveje - Vis genveje + Genveje + Vis genveje - Brug listevisning - Tillad på rodniveau + Brug listevisning + Tillad på rodniveau @@ -546,13 +542,17 @@ Kunne ikke gemme web.config filen. Du bedes venligst manuelt ændre database forbindelses strengen. Din database er blevet fundet og identificeret som Database konfiguration - + installér knappen for at installere Umbraco %0% databasen - ]]> + ]]> + installér knappen for at installere Umbraco %0% databasen]]> Næste for at fortsætte.]]> - Databasen er ikke fundet. Kontrollér venligst at informationen i database forbindelsesstrengen i "web.config" filen er korrekt.

    -

    For at fortsætte bedes du venligst rette "web.config" filen (ved at bruge Visual Studio eller dit favoritprogram), scroll til bunden, tilføj forbindelsesstrengen til din database i feltet som hedder "umbracoDbDSN" og gem filen.

    Klik på Forsøg igen knappen når du er færdig.
    Mere information om at redigere web.config her.

    ]]>
    + + Databasen er ikke fundet. Kontrollér venligst at informationen i database forbindelsesstrengen i "web.config" filen er korrekt.

    +

    For at fortsætte bedes du venligst rette "web.config" filen (ved at bruge Visual Studio eller dit favoritprogram), scroll til bunden, tilføj forbindelsesstrengen til din database i feltet som hedder "umbracoDbDSN" og gem filen.

    Klik på Forsøg igen knappen når du er færdig.
    Mere information om at redigere web.config her.

    ]]> +
    Kontakt venligst din ISP hvis det er nødvendigt. Hvis du installerer på en lokal maskine eller server kan du muligvis få informationerne fra din systemadministrator.]]> Tryk på Opgradér knappen for at opgradere din database til Umbraco %0%

    Bare rolig - intet indhold vil blive slettet og alt vil stadig fungere bagefter!

    ]]>
    Tryk på Næste for at fortsætte.]]> @@ -598,8 +598,10 @@ Yderligere hjælpe og informationer Få hjælp fra vores prisvindende fællesskab, gennemse dokumentationen eller se nogle gratis videoer om hvordan du opsætter et simpelt site, hvordan du bruger pakker og en 'quick guide' til Umbraco terminologier]]> Umbraco %0% er installeret og klar til brug /web.config filen og opdatére 'AppSetting' feltet UmbracoConfigurationStatus i bunden til '%0%'.]]> - komme igang med det samme ved at klikke på "Start Umbraco" knappen nedenfor.
    Hvis du er ny med Umbraco, kan du finde masser af ressourcer på vores 'getting started' sider. -]]>
    + + komme igang med det samme ved at klikke på "Start Umbraco" knappen nedenfor.
    Hvis du er ny med Umbraco, kan du finde masser af ressourcer på vores 'getting started' sider. +]]> +
    Start UmbracoFor at administrere dit website skal du blot åbne Umbraco administrationen og begynde at tilføje indhold, opdatere skabelonerne og stylesheets'ene eller tilføje ny funktionalitet.]]> Forbindelse til databasen fejlede. Umbraco Version 3 @@ -674,12 +676,15 @@ Gå til http://%4%/#/content/content/edit/%5% for at redigere. Ha' en dejlig dag! Mange hilsner fra Umbraco robotten - ]]> - Hej %0%

    + ]]> +
    + + Hej %0%

    Dette er en automatisk mail for at informere dig om at opgaven '%1%' er blevet udførtpå siden '%2%' af brugeren '%3%'

    Opdateringssammendrag:

    %6%

    Hav en fortsat god dag!

    De bedste hilsner fra umbraco robotten

    ]]>
    +      RET       

    Opdateringssammendrag:

    %6%

    Hav en fortsat god dag!

    De bedste hilsner fra umbraco robotten

    ]]> + [%0%] Notificering om %1% udført på %2% Notificeringer @@ -700,8 +705,10 @@ Mange hilsner fra Umbraco robotten Pakken blev fjernet Pakken er på succefuld vis blevet fjernet Afinstallér pakke - -Bemærk: at dokumenter og medier som afhænger af denne pakke vil muligvis holde op med at virke, så vær forsigtig. Hvis i tvivl, kontakt personen som har udviklet pakken.]]> + + +Bemærk: at dokumenter og medier som afhænger af denne pakke vil muligvis holde op med at virke, så vær forsigtig. Hvis i tvivl, kontakt personen som har udviklet pakken.]]> + Download opdatering fra opbevaringsbasen Opdatér pakke Opdateringsinstrukser @@ -734,6 +741,19 @@ Mange hilsner fra Umbraco robotten Hvis du blot ønsker at opsætte simpel beskyttelse ved hjælp af et enkelt login og kodeord + Udgivelsen kunne ikke udgives da publiceringsdato er sat + + + + + + + Udgivelsen fejlede fordi en overordnet side ikke er publiceret + %0% kunne ikke udgives, fordi et 3. parts modul annullerede handlingen Medtag ikke-udgivede undersider Publicerer - vent venligst... @@ -807,58 +827,58 @@ Mange hilsner fra Umbraco robotten - Kompositioner - Du har ikke tilføjet nogle faner - Tilføj ny fane - Tilføj endnu en fane - Nedarvet fra - Tilføj property - Påkrævet label + Kompositioner + Du har ikke tilføjet nogle faner + Tilføj ny fane + Tilføj endnu en fane + Nedarvet fra + Tilføj property + Påkrævet label - Aktiver listevisning - Konfigurer indholdet til at blive vist i en sorterbar og søgbar liste, dens børn vil ikke blive vist i træet + Aktiver listevisning + Konfigurer indholdet til at blive vist i en sorterbar og søgbar liste, dens børn vil ikke blive vist i træet - Tilladte skabeloner - Vælg hvilke skabeloner der er tilladt at bruge på dette indhold + Tilladte skabeloner + Vælg hvilke skabeloner der er tilladt at bruge på dette indhold - Tillad på rodniveau - Kun dokumenttyper med denne indstilling aktiveret oprettes i rodniveau under inhold og mediearkiv - Ja – indhold af denne type er tilladt i roden + Tillad på rodniveau + Kun dokumenttyper med denne indstilling aktiveret oprettes i rodniveau under inhold og mediearkiv + Ja – indhold af denne type er tilladt i roden - Tilladte typer - Tillad at oprette indhold af en specifik type under denne + Tilladte typer + Tillad at oprette indhold af en specifik type under denne - Vælg child node + Vælg child node - Nedarv faner og egenskaber fra en anden dokumenttype. Nye faner vil blive tilføjet den nuværende dokumenttype eller sammenflettet hvis fanenavnene er ens. - Indholdstypen bliver brugt i en komposition og kan derfor ikke blive anvendt som komposition - Der er ingen indholdstyper tilgængelige at bruge som komposition + Nedarv faner og egenskaber fra en anden dokumenttype. Nye faner vil blive tilføjet den nuværende dokumenttype eller sammenflettet hvis fanenavnene er ens. + Indholdstypen bliver brugt i en komposition og kan derfor ikke blive anvendt som komposition + Der er ingen indholdstyper tilgængelige at bruge som komposition - Tilgængelige editors - Genbrug - Editor indstillinger + Tilgængelige editors + Genbrug + Editor indstillinger - Konfiguration + Konfiguration - Ja, slet + Ja, slet - blev flyttet til - Vælg hvor - skal flyttes til + blev flyttet til + Vælg hvor + skal flyttes til - Alle dokumenttyper - Alle dokumenter - Alle medier + Alle dokumenttyper + Alle dokumenter + Alle medier - som benytter denne dokumenttype vil blive slettet permanent. Bekræft at du også vil slette dem. - som benytter denne medietype vil blive slettet permanent. Bekræft at du også vil slette dem. - som benytter denne medlemstype vil blive slettet permanent. Bekræft at du også vil slette dem. + som benytter denne dokumenttype vil blive slettet permanent. Bekræft at du også vil slette dem. + som benytter denne medietype vil blive slettet permanent. Bekræft at du også vil slette dem. + som benytter denne medlemstype vil blive slettet permanent. Bekræft at du også vil slette dem. - og alle dokumenter, som benytter denne type - og alle medier, som benytter denne type - og alle medlemmer, som benytter denne type + og alle dokumenter, som benytter denne type + og alle medier, som benytter denne type + og alle medlemmer, som benytter denne type - der bruger denne editor vil blive opdateret med de nye indstillinger + der bruger denne editor vil blive opdateret med de nye indstillinger @@ -922,8 +942,6 @@ Mange hilsner fra Umbraco robotten Annulleret Handlingen blev annulleret af et 3. part tilføjelsesprogram - Udgivelsen blev standset af et 3. parts modul - Udgivelsen kunne ikke udgives da publiceringsdato er sat Property type eksisterer allerede Egenskabstype oprettet DataType: %1%]]> @@ -937,7 +955,6 @@ Mange hilsner fra Umbraco robotten Stylesheet gemt uden fejl Datatype gemt Ordbogsnøgle gemt - Udgivelsen fejlede fordi en overordnet side ikke er publiceret Indhold publiceret og nu synligt for besøgende Indhold gemt @@ -1141,11 +1158,28 @@ Mange hilsner fra Umbraco robotten Session udløber - Validation - Valider som email - Valider som tal - Valider som Url - ...eller indtast din egen validering - Feltet er påkrævet + Validation + Valider som email + Valider som tal + Valider som Url + ...eller indtast din egen validering + Feltet er påkrævet + + + Slå URL tracker fra + Slå URL tracker til + Original URL + Viderestillet til + Der er ikke lavet nogen viderestillinger + Når en udgivet side bliver omdøbt eller flyttet, vil en viderestilling automatisk blive lavet til den nye side. + Fjern + Er du sikker på at du vil fjerne viderestillingen fra '%0%' til '%1%'? + Viderestillings URL fjernet. + Fejl under fjernelse af viderestillings URL. + Er du sikker på at du vil slå URL trackeren fra? + URL tracker er nu slået fra. + Der opstod en fejl under forsøget på at slå URL trackeren fra, der findes mere information i logfilen. + URL tracker er nu slået fra. + Der opstod en fejl under forsøget på at slå URL trackeren til, der findes mere information i logfilen. diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 5f0180259a..87012194e3 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -28,6 +28,9 @@ Reload Republish entire site Restore + Set permissions for the page %0% + Choose where to move + to in the tree structure below Permissions Rollback Send To Publish @@ -287,18 +290,12 @@ Pick item View Cache Item Create folder... - Relate to original + Include descendants The friendliest community - Link to page - Opens the linked document in a new window or tab - Opens the linked document in the full body of the window - Opens the linked document in the parent frame - Link to media - Select media Select icon Select item @@ -308,19 +305,14 @@ Select member Select member group No icons were found - There are no parameters for this macro - External login providers Exception Details Stacktrace Inner Exception - Link your Un-Link your - account - Select editor @@ -826,6 +818,7 @@ To manage your website, simply open the Umbraco back office and start adding con Installing... Restarting, please wait... All done, your browser will now refresh, please wait... + Please click finish to complete installation and reload page. Paste with full formatting (Not recommended) @@ -1345,7 +1338,7 @@ To manage your website, simply open the Umbraco back office and start adding con Custom errors successfully set to '%0%'. MacroErrors are set to '%0%'. - MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely when there's any errors in macros. Rectifying this will set the value to '%1%'. + MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely if there are any errors in macros. Rectifying this will set the value to '%1%'. MacroErrors are now set to '%0%'. + Zatwierdź Typ Szukaj W górę @@ -356,8 +356,8 @@ Możesz dodać dodatkowe języki w menu "Języki" po lewej stronie.]]> Witaj... Szerokość Tak - Reorder - I am done reordering + Zmień kolejność + Kolejność została zmieniona Kolor tła @@ -789,7 +789,7 @@ Miłego dnia!]]> Administrator Pole kategorii - TRANSLATE ME: 'Change Your Password' + Zmień hasło! TRANSLATE ME: 'You can change your password for accessing the Umbraco Back Office by filling out the form below and click the 'Change Password' button' Zawartość Opis @@ -803,10 +803,10 @@ Miłego dnia!]]> Sekcje Wyłącz dostęp do Umbraco Hasło - TRANSLATE ME: 'Your password has been changed!' - TRANSLATE ME: 'Please confirm the new password' - TRANSLATE ME: 'Enter your new password' - TRANSLATE ME: 'Your new password cannot be blank!' + Twoje hasło zostało zmienione! + Proszę potwierdź nowe hasło! + Wprowadź nowe hasło + Nowe hasło nie może byc puste! TRANSLATE ME: 'There was a difference between the new password and the confirmed password. Please try again!' TRANSLATE ME: 'The confirmed password doesn't match the new password!' Zastąp prawa dostępu dla węzłów potomnych diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index 071f5fbd05..1c4592adb0 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -340,18 +340,11 @@ Выберите элемент Просмотр элемента кэша Создать папку... - Связать с оригиналом Самое дружелюбное сообщество - Ссылка на страницу - Открывает документ по ссылке в новом окне или вкладке браузера - Открывает документ по ссылке в полноэкранном режиме - Открывает документ по ссылке в родительском фрейме - Ссылка на медиа-файл - Выбрать медиа Выбрать значок Выбрать элемент @@ -360,19 +353,14 @@ Выбрать содержимое Выбрать участника Выбрать группу участников - Это макрос без параметров - Провайдеры аутентификации Подробное сообщение об ошибке Трассировка стека Внутренняя ошибка - Связать Разорвать связь - учетную запись - Выбрать редактор @@ -380,6 +368,12 @@ Ниже Вы можете указать различные переводы данной статьи словаря '%0%'
    Добавить другие языки можно, воспользовавшись пунктом 'Языки' в меню слева ]]> Название языка (культуры) + Редактировать элемент (ключ) словаря + + + Допустим как корневой @@ -655,7 +649,7 @@ Медиа - всего в XML: %0%, всего: %1%Б с ошибками: %2% Содержимое - всего в XML: %0%, всего опубликовано: %1%, с ошибками: %2% - Сертификат Вашего сайта отмечен как проверенный. + Сертификат Вашего веб-сайта отмечен как проверенный. Ошибка проверки сертификата: '%0%' Ошибка проверки адреса URL %0% - '%1%' Сейчас Вы %0% просматриваете сайт, используя протокол HTTPS. diff --git a/src/Umbraco.Web.UI/umbraco_client/Application/UmbracoApplicationActions.js b/src/Umbraco.Web.UI/umbraco_client/Application/UmbracoApplicationActions.js index e40e59e792..f8d1061cbe 100644 --- a/src/Umbraco.Web.UI/umbraco_client/Application/UmbracoApplicationActions.js +++ b/src/Umbraco.Web.UI/umbraco_client/Application/UmbracoApplicationActions.js @@ -281,7 +281,7 @@ Umbraco.Application.Actions = function() { actionRePublish: function() { /// - UmbClientMgr.openModalWindow('dialogs/republish.aspx?rnd=' + this._utils.generateRandom(), 'Republishing entire site', true, 450, 210); + UmbClientMgr.openModalWindow('dialogs/republish.aspx?rnd=' + this._utils.generateRandom(), uiKeys['actions_republish'], true, 450, 210); }, actionAssignDomain: function() { diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index 061fc57775..f254028ceb 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -353,5 +353,19 @@ namespace Umbraco.Web.Cache } #endregion + + #region Relation type cache + + public static void RefreshRelationTypeCache(this DistributedCache dc, int id) + { + dc.Refresh(RelationTypeCacheRefresher.UniqueId, id); + } + + public static void RemoveRelationTypeCache(this DistributedCache dc, int id) + { + dc.Remove(RelationTypeCacheRefresher.UniqueId, id); + } + + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/RelationTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/RelationTypeCacheRefresher.cs new file mode 100644 index 0000000000..798a5e5ebb --- /dev/null +++ b/src/Umbraco.Web/Cache/RelationTypeCacheRefresher.cs @@ -0,0 +1,56 @@ +using System; +using Umbraco.Core.Cache; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; + +namespace Umbraco.Web.Cache +{ + public sealed class RelationTypeCacheRefresher : CacheRefresherBase + { + public RelationTypeCacheRefresher(CacheHelper cacheHelper) + : base(cacheHelper) + { } + + #region Define + + protected override RelationTypeCacheRefresher Instance => this; + + public static readonly Guid UniqueId = Guid.Parse("D8375ABA-4FB3-4F86-B505-92FBA1B6F7C9"); + + public override Guid RefresherUniqueId => UniqueId; + + public override string Name => "Relation Type Cache Refresher"; + + #endregion + + #region Refresher + + public override void RefreshAll() + { + ClearAllIsolatedCacheByEntityType(); + base.RefreshAll(); + } + + public override void Refresh(int id) + { + var cache = CacheHelper.IsolatedRuntimeCache.GetCache(); + if (cache) cache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); + base.Refresh(id); + } + + public override void Refresh(Guid id) + { + throw new NotSupportedException(); + //base.Refresh(id); + } + + public override void Remove(int id) + { + var cache = CacheHelper.IsolatedRuntimeCache.GetCache(); + if (cache) cache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); + base.Remove(id); + } + + #endregion + } +} diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 2a9fc3e8a1..67539734fd 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -517,8 +517,17 @@ namespace Umbraco.Web.Editors { var mediaType = Constants.Conventions.MediaTypes.File; - if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext)) - mediaType = Constants.Conventions.MediaTypes.Image; + if (result.FormData["contentTypeAlias"] == Constants.Conventions.MediaTypes.AutoSelect) + { + if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext)) + { + mediaType = Constants.Conventions.MediaTypes.Image; + } + } + else + { + mediaType = result.FormData["contentTypeAlias"]; + } //TODO: make the media item name "nice" since file names could be pretty ugly, we have // string extensions to do much of this but we'll need: diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs index 3cef732311..4dcdf91c2f 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security [HealthCheck( "92ABBAA2-0586-4089-8AE2-9A843439D577", "Excessive Headers", - Description = "Checks to see if your site is revealing information in it's headers that gives away unnecessary details about the technology used to build and host it.", + Description = "Checks to see if your site is revealing information in its headers that gives away unnecessary details about the technology used to build and host it.", Group = "Security")] public class ExcessiveHeadersCheck : HealthCheck { diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index ee169c27c8..2d50c6e7fe 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -42,38 +42,18 @@ namespace Umbraco.Web.Models.Mapping //FROM IContent TO ContentItemDisplay config.CreateMap() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember( - dto => dto.Updater, - expression => expression.ResolveUsing(new CreatorResolver(_userService))) - .ForMember( - dto => dto.Icon, - expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember( - dto => dto.ContentTypeAlias, - expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember( - dto => dto.ContentTypeName, - expression => expression.MapFrom(content => content.ContentType.Name)) - .ForMember( - dto => dto.IsContainer, - expression => expression.MapFrom(content => content.ContentType.IsContainer)) - .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) - .ForMember( - dto => dto.Trashed, - expression => expression.MapFrom(content => content.Trashed)) - .ForMember( - dto => dto.PublishDate, - expression => expression.MapFrom(content => GetPublishedDate(_contentService, content))) - .ForMember( - dto => dto.TemplateAlias, expression => expression.MapFrom(content => content.Template.Alias)) - .ForMember( - dto => dto.HasPublishedVersion, - expression => expression.MapFrom(content => content.HasPublishedVersion)) - .ForMember( - dto => dto.Urls, + .ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(display => display.Updater, expression => expression.ResolveUsing(new CreatorResolver(_userService))) + .ForMember(display => display.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name)) + .ForMember(display => display.IsContainer, expression => expression.MapFrom(content => content.ContentType.IsContainer)) + .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) + .ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed)) + .ForMember(display => display.PublishDate, expression => expression.MapFrom(content => GetPublishedDate(_contentService, content))) + .ForMember(display => display.TemplateAlias, expression => expression.MapFrom(content => content.Template.Alias)) + .ForMember(display => display.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) + .ForMember(display => display.Urls, expression => expression.MapFrom(content => UmbracoContext.Current == null ? new[] {"Cannot generate urls without a current Umbraco Context"} @@ -87,47 +67,27 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(_textService))) .ForMember(display => display.AllowedActions, expression => expression.ResolveUsing( new ActionButtonsResolver(new Lazy(() => _userService)))) - .AfterMap((media, display) => AfterMap(media, display, _dataTypeService, _textService, - _contentTypeService, _contentService)); + .AfterMap((content, display) => AfterMap(content, display, _dataTypeService, _textService, _contentTypeService, _contentService)); //FROM IContent TO ContentItemBasic config.CreateMap>() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember( - dto => dto.Updater, - expression => expression.ResolveUsing(new CreatorResolver(_userService))) - .ForMember( - dto => dto.Icon, - expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember( - dto => dto.Trashed, - expression => expression.MapFrom(content => content.Trashed)) - .ForMember( - dto => dto.HasPublishedVersion, - expression => expression.MapFrom(content => content.HasPublishedVersion)) - .ForMember( - dto => dto.ContentTypeAlias, - expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(display => display.Alias, expression => expression.Ignore()); + .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(dto => dto.Updater, expression => expression.ResolveUsing(new CreatorResolver(_userService))) + .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember(dto => dto.Trashed, expression => expression.MapFrom(content => content.Trashed)) + .ForMember(dto => dto.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) + .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(dto => dto.Alias, expression => expression.Ignore()); //FROM IContent TO ContentItemDto config.CreateMap>() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember( - dto => dto.HasPublishedVersion, - expression => expression.MapFrom(content => content.HasPublishedVersion)) - .ForMember(display => display.Updater, expression => expression.Ignore()) - .ForMember(display => display.Icon, expression => expression.Ignore()) - .ForMember(display => display.Alias, expression => expression.Ignore()); - - + .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(dto => dto.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) + .ForMember(dto => dto.Updater, expression => expression.Ignore()) + .ForMember(dto => dto.Icon, expression => expression.Ignore()) + .ForMember(dto => dto.Alias, expression => expression.Ignore()); } - /// /// Maps the generic tab with custom properties for content /// @@ -137,7 +97,7 @@ namespace Umbraco.Web.Models.Mapping /// /// /// - private static void AfterMap(IContent content, ContentItemDisplay display, IDataTypeService dataTypeService, + private static void AfterMap(IContent content, ContentItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, IContentTypeService contentTypeService, IContentService contentService) { //map the IsChildOfListView (this is actually if it is a descendant of a list view!) @@ -165,7 +125,6 @@ namespace Umbraco.Web.Models.Mapping display.IsChildOfListView = ancesctorListView != null; } } - //map the tree node url if (HttpContext.Current != null) @@ -174,9 +133,9 @@ namespace Umbraco.Web.Models.Mapping var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Id.ToString(), null)); display.TreeNodeUrl = url; } - + //fill in the template config to be passed to the template drop down. - var templateItemConfig = new Dictionary { { "", "Choose..." } }; + var templateItemConfig = new Dictionary {{"", "Choose..."}}; foreach (var t in content.ContentType.AllowedTemplates .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)) { @@ -187,7 +146,7 @@ namespace Umbraco.Web.Models.Mapping { TabsAndPropertiesResolver.AddListView(display, "content", dataTypeService, localizedText); } - + var properties = new List { new ContentPropertyDisplay @@ -197,7 +156,7 @@ namespace Umbraco.Web.Models.Mapping Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), View = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View }, - new ContentPropertyDisplay + new ContentPropertyDisplay { Alias = string.Format("{0}releasedate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = localizedText.Localize("content/releaseDate"), @@ -209,7 +168,7 @@ namespace Umbraco.Web.Models.Mapping {"offsetTime", "1"} } //TODO: Fix up hard coded datepicker - } , + }, new ContentPropertyDisplay { Alias = string.Format("{0}expiredate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), @@ -260,21 +219,21 @@ namespace Umbraco.Web.Models.Mapping var docTypeLink = string.Format("#/settings/documenttypes/edit/{0}", currentDocumentTypeId); //Replace the doc type property - var docTypeProp = genericProperties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - docTypeProp.Value = new List + var docTypeProperty = genericProperties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + docTypeProperty.Value = new List { new { linkText = currentDocumentTypeName, url = docTypeLink, - target = "_self", icon = "icon-item-arrangement" + target = "_self", + icon = "icon-item-arrangement" } }; //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor - docTypeProp.View = "urllist"; + docTypeProperty.View = "urllist"; } }); - } /// @@ -319,13 +278,13 @@ namespace Umbraco.Web.Models.Mapping var svc = _userService.Value; var permissions = svc.GetPermissions( - //TODO: This is certainly not ideal usage here - perhaps the best way to deal with this in the future is - // with the IUmbracoContextAccessor. In the meantime, if used outside of a web app this will throw a null - // refrence exception :( - UmbracoContext.Current.Security.CurrentUser, - // Here we need to do a special check since this could be new content, in which case we need to get the permissions - // from the parent, not the existing one otherwise permissions would be coming from the root since Id is 0. - source.HasIdentity ? source.Id : source.ParentId) + //TODO: This is certainly not ideal usage here - perhaps the best way to deal with this in the future is + // with the IUmbracoContextAccessor. In the meantime, if used outside of a web app this will throw a null + // refrence exception :( + UmbracoContext.Current.Security.CurrentUser, + // Here we need to do a special check since this could be new content, in which case we need to get the permissions + // from the parent, not the existing one otherwise permissions would be coming from the root since Id is 0. + source.HasIdentity ? source.Id : source.ParentId) .FirstOrDefault(); return permissions == null @@ -333,6 +292,5 @@ namespace Umbraco.Web.Models.Mapping : permissions.AssignedPermissions.Where(x => x.Length == 1).Select(x => x.ToUpperInvariant()[0]); } } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs index f686297927..7394f9b099 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs @@ -39,22 +39,12 @@ namespace Umbraco.Web.Models.Mapping { //FROM IMedia TO MediaItemDisplay config.CreateMap() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember( - dto => dto.Icon, - expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember( - dto => dto.ContentTypeAlias, - expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(display => display.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) - .ForMember( - dto => dto.Trashed, - expression => expression.MapFrom(content => content.Trashed)) - .ForMember( - dto => dto.ContentTypeName, - expression => expression.MapFrom(content => content.ContentType.Name)) + .ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed)) + .ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name)) .ForMember(display => display.Properties, expression => expression.Ignore()) .ForMember(display => display.TreeNodeUrl, expression => expression.Ignore()) .ForMember(display => display.Notifications, expression => expression.Ignore()) @@ -63,39 +53,29 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.Updater, expression => expression.Ignore()) .ForMember(display => display.Alias, expression => expression.Ignore()) .ForMember(display => display.IsContainer, expression => expression.Ignore()) - .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()) + .ForMember(display => display.HasPublishedVersion, expression => expression.Ignore()) .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(_textService))) .AfterMap((media, display) => AfterMap(media, display, _dataTypeService, _textService, _logger, _mediaService)); //FROM IMedia TO ContentItemBasic config.CreateMap>() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember( - dto => dto.Icon, - expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember( - dto => dto.Trashed, - expression => expression.MapFrom(content => content.Trashed)) - .ForMember( - dto => dto.ContentTypeAlias, - expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(x => x.Published, expression => expression.Ignore()) - .ForMember(x => x.Updater, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()); + .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember(dto => dto.Trashed, expression => expression.MapFrom(content => content.Trashed)) + .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(dto => dto.Published, expression => expression.Ignore()) + .ForMember(dto => dto.Updater, expression => expression.Ignore()) + .ForMember(dto => dto.Alias, expression => expression.Ignore()) + .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); //FROM IMedia TO ContentItemDto config.CreateMap>() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(x => x.Published, expression => expression.Ignore()) - .ForMember(x => x.Updater, expression => expression.Ignore()) - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()); + .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(dto => dto.Published, expression => expression.Ignore()) + .ForMember(dto => dto.Updater, expression => expression.Ignore()) + .ForMember(dto => dto.Icon, expression => expression.Ignore()) + .ForMember(dto => dto.Alias, expression => expression.Ignore()) + .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); } private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, ILogger logger, IMediaService mediaService) @@ -165,8 +145,28 @@ namespace Umbraco.Web.Models.Mapping genericProperties.Add(link); } - TabsAndPropertiesResolver.MapGenericProperties(media, display, localizedText, genericProperties); - } + TabsAndPropertiesResolver.MapGenericProperties(media, display, localizedText, genericProperties, properties => + { + if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null + && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + { + var mediaTypeLink = string.Format("#/settings/mediatypes/edit/{0}", media.ContentTypeId); + //Replace the doctype property + var docTypeProperty = properties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + docTypeProperty.Value = new List + { + new + { + linkText = media.ContentType.Name, + url = mediaTypeLink, + target = "_self", + icon = "icon-item-arrangement" + } + }; + docTypeProperty.View = "urllist"; + } + }); + } } } diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs index 0c38879c7d..5eca1843d0 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -40,10 +40,10 @@ namespace Umbraco.Web.Models.Mapping //FROM MembershipUser TO MediaItemDisplay - used when using a non-umbraco membership provider config.CreateMap() .ConvertUsing(user => - { - var member = Mapper.Map(user); - return Mapper.Map(member); - }); + { + var member = Mapper.Map(user); + return Mapper.Map(member); + }); //FROM MembershipUser TO IMember - used when using a non-umbraco membership provider config.CreateMap() @@ -73,23 +73,13 @@ namespace Umbraco.Web.Models.Mapping //FROM IMember TO MediaItemDisplay config.CreateMap() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember( - dto => dto.Icon, - expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember( - dto => dto.ContentTypeAlias, - expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember( - dto => dto.ContentTypeName, - expression => expression.MapFrom(content => content.ContentType.Name)) + .ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(display => display.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name)) .ForMember(display => display.Properties, expression => expression.Ignore()) - .ForMember(display => display.Tabs, - expression => expression.ResolveUsing(new MemberTabsAndPropertiesResolver(_textService))) - .ForMember(display => display.MemberProviderFieldMapping, - expression => expression.ResolveUsing(new MemberProviderFieldMappingResolver())) + .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new MemberTabsAndPropertiesResolver(_textService))) + .ForMember(display => display.MemberProviderFieldMapping, expression => expression.ResolveUsing(new MemberProviderFieldMappingResolver())) .ForMember(display => display.MembershipScenario, expression => expression.ResolveUsing(new MembershipScenarioMappingResolver(new Lazy(() => _memberTypeService)))) .ForMember(display => display.Notifications, expression => expression.Ignore()) @@ -101,31 +91,21 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.Trashed, expression => expression.Ignore()) .ForMember(display => display.IsContainer, expression => expression.Ignore()) .ForMember(display => display.TreeNodeUrl, expression => expression.Ignore()) - .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()) + .ForMember(display => display.HasPublishedVersion, expression => expression.Ignore()) .AfterMap((member, display) => MapGenericCustomProperties(_memberService, member, display, _textService)); //FROM IMember TO MemberBasic config.CreateMap() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember( - dto => dto.Icon, - expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember( - dto => dto.ContentTypeAlias, - expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember( - dto => dto.Email, - expression => expression.MapFrom(content => content.Email)) - .ForMember( - dto => dto.Username, - expression => expression.MapFrom(content => content.Username)) - .ForMember(display => display.Trashed, expression => expression.Ignore()) - .ForMember(x => x.Published, expression => expression.Ignore()) - .ForMember(x => x.Updater, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()); + .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(dto => dto.Email, expression => expression.MapFrom(content => content.Email)) + .ForMember(dto => dto.Username, expression => expression.MapFrom(content => content.Username)) + .ForMember(dto => dto.Trashed, expression => expression.Ignore()) + .ForMember(dto => dto.Published, expression => expression.Ignore()) + .ForMember(dto => dto.Updater, expression => expression.Ignore()) + .ForMember(dto => dto.Alias, expression => expression.Ignore()) + .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); //FROM MembershipUser TO MemberBasic config.CreateMap() @@ -134,41 +114,31 @@ namespace Umbraco.Web.Models.Mapping .ForMember(member => member.CreateDate, expression => expression.MapFrom(user => user.CreationDate)) .ForMember(member => member.UpdateDate, expression => expression.MapFrom(user => user.LastActivityDate)) .ForMember(member => member.Key, expression => expression.MapFrom(user => user.ProviderUserKey.TryConvertTo().Result.ToString("N"))) - .ForMember( - dto => dto.Owner, - expression => expression.UseValue(new UserBasic {Name = "Admin", UserId = 0})) - .ForMember( - dto => dto.Icon, - expression => expression.UseValue("icon-user")) + .ForMember(member => member.Owner, expression => expression.UseValue(new UserBasic {Name = "Admin", UserId = 0})) + .ForMember(member => member.Icon, expression => expression.UseValue("icon-user")) .ForMember(member => member.Name, expression => expression.MapFrom(user => user.UserName)) - .ForMember( - dto => dto.Email, - expression => expression.MapFrom(content => content.Email)) - .ForMember( - dto => dto.Username, - expression => expression.MapFrom(content => content.UserName)) + .ForMember(member => member.Email, expression => expression.MapFrom(content => content.Email)) + .ForMember(member => member.Username, expression => expression.MapFrom(content => content.UserName)) .ForMember(member => member.Properties, expression => expression.Ignore()) .ForMember(member => member.ParentId, expression => expression.Ignore()) .ForMember(member => member.Path, expression => expression.Ignore()) .ForMember(member => member.SortOrder, expression => expression.Ignore()) .ForMember(member => member.AdditionalData, expression => expression.Ignore()) - .ForMember(x => x.Published, expression => expression.Ignore()) - .ForMember(x => x.Updater, expression => expression.Ignore()) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(x => x.ContentTypeAlias, expression => expression.Ignore()) + .ForMember(member => member.Published, expression => expression.Ignore()) + .ForMember(member => member.Updater, expression => expression.Ignore()) + .ForMember(member => member.Trashed, expression => expression.Ignore()) + .ForMember(member => member.Alias, expression => expression.Ignore()) + .ForMember(member => member.ContentTypeAlias, expression => expression.Ignore()) .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()); //FROM IMember TO ContentItemDto config.CreateMap>() - .ForMember( - dto => dto.Owner, - expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(x => x.Published, expression => expression.Ignore()) - .ForMember(x => x.Updater, expression => expression.Ignore()) - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()) + .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) + .ForMember(dto => dto.Published, expression => expression.Ignore()) + .ForMember(dto => dto.Updater, expression => expression.Ignore()) + .ForMember(dto => dto.Icon, expression => expression.Ignore()) + .ForMember(dto => dto.Alias, expression => expression.Ignore()) + .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()) //do no map the custom member properties (currently anyways, they were never there in 6.x) .ForMember(dto => dto.Properties, expression => expression.ResolveUsing(new MemberDtoPropertiesValueResolver())); @@ -253,8 +223,28 @@ namespace Umbraco.Web.Models.Mapping } }; + TabsAndPropertiesResolver.MapGenericProperties(member, display, localizedText, genericProperties, properties => + { + if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null + && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + { + var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", member.ContentTypeId); - TabsAndPropertiesResolver.MapGenericProperties(member, display, localizedText, genericProperties); + //Replace the doctype property + var docTypeProperty = properties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + docTypeProperty.Value = new List + { + new + { + linkText = member.ContentType.Name, + url = memberTypeLink, + target = "_self", + icon = "icon-item-arrangement" + } + }; + docTypeProperty.View = "urllist"; + } + }); //check if there's an approval field var provider = membersProvider as IUmbracoMemberTypeMembershipProvider; @@ -267,7 +257,6 @@ namespace Umbraco.Web.Models.Mapping prop.Value = 1; } } - } /// @@ -276,6 +265,7 @@ namespace Umbraco.Web.Models.Mapping /// /// /// + /// /// /// /// If the membership provider installed is the umbraco membership provider, then we will allow changing the username, however if @@ -285,11 +275,11 @@ namespace Umbraco.Web.Models.Mapping internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, MemberDisplay display, ILocalizedTextService localizedText) { var prop = new ContentPropertyDisplay - { - Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("login"), - Value = display.Username - }; + { + Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("login"), + Value = display.Username + }; var scenario = memberService.GetMembershipScenario(); @@ -342,8 +332,8 @@ namespace Umbraco.Web.Models.Mapping var exclude = defaultProps.Select(x => x.Value.Alias).ToArray(); return source.Properties - .Where(x => exclude.Contains(x.Alias) == false) - .Select(Mapper.Map); + .Where(x => exclude.Contains(x.Alias) == false) + .Select(Mapper.Map); } } @@ -396,7 +386,7 @@ namespace Umbraco.Web.Models.Mapping } else { - var umbracoProvider = (IUmbracoMemberTypeMembershipProvider)provider; + var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider; //This is kind of a hack because a developer is supposed to be allowed to set their property editor - would have been much easier // if we just had all of the membeship provider fields on the member table :( @@ -410,8 +400,6 @@ namespace Umbraco.Web.Models.Mapping return result; } - - } } @@ -434,8 +422,8 @@ namespace Umbraco.Web.Models.Mapping } var memberType = _memberTypeService.Value.Get(Constants.Conventions.MemberTypes.DefaultAlias); return memberType != null - ? MembershipScenario.CustomProviderWithUmbracoLink - : MembershipScenario.StandaloneCustomProvider; + ? MembershipScenario.CustomProviderWithUmbracoLink + : MembershipScenario.StandaloneCustomProvider; } } @@ -459,7 +447,7 @@ namespace Umbraco.Web.Models.Mapping } else { - var umbracoProvider = (IUmbracoMemberTypeMembershipProvider)provider; + var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider; return new Dictionary { @@ -468,10 +456,7 @@ namespace Umbraco.Web.Models.Mapping {Constants.Conventions.Member.Comments, umbracoProvider.CommentPropertyTypeAlias} }; } - - } } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 72f81b97fe..7eb48817cc 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -7,96 +7,101 @@ using Umbraco.Core.Logging; namespace Umbraco.Web.Templates { - //NOTE: I realize there is only one class in this namespace but I'm pretty positive that there will be more classes in - //this namespace once we start migrating and cleaning up more code. + //NOTE: I realize there is only one class in this namespace but I'm pretty positive that there will be more classes in + //this namespace once we start migrating and cleaning up more code. - /// - /// Utility class used for templates - /// - public static class TemplateUtilities - { + /// + /// Utility class used for templates + /// + public static class TemplateUtilities + { //TODO: Pass in an Umbraco context!!!!!!!! Don't rely on the singleton so things are more testable internal static string ParseInternalLinks(string text, bool preview) - { + { using (UmbracoContext.Current.ForcedPreview(preview)) // force for url provider { text = ParseInternalLinks(text); } return text; - } + } - /// - /// Parses the string looking for the {localLink} syntax and updates them to their correct links. - /// - /// - /// - public static string ParseInternalLinks(string text) - { + /// + /// Parses the string looking for the {localLink} syntax and updates them to their correct links. + /// + /// + /// + public static string ParseInternalLinks(string text) + { //TODO: Pass in an Umbraco context!!!!!!!! Don't rely on the singleton so things are more testable, better yet, pass in urlprovider, routing context, separately - //don't attempt to proceed without a context as we cannot lookup urls without one + //don't attempt to proceed without a context as we cannot lookup urls without one if (UmbracoContext.Current == null) - { - return text; - } + { + return text; + } - var urlProvider = UmbracoContext.Current.UrlProvider; + var urlProvider = UmbracoContext.Current.UrlProvider; - // Parse internal links - var tags = Regex.Matches(text, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - foreach (Match tag in tags) - if (tag.Groups.Count > 0) - { - var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1); - var newLink = urlProvider.GetUrl(int.Parse(id)); - text = text.Replace(tag.Value, "href=\"" + newLink); - } + // Parse internal links + var tags = Regex.Matches(text, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + foreach (Match tag in tags) + if (tag.Groups.Count > 0) + { + var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1); + var newLink = urlProvider.GetUrl(int.Parse(id)); + text = text.Replace(tag.Value, "href=\"" + newLink); + } return text; - } + } - // static compiled regex for faster performance - private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + // static compiled regex for faster performance + private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - /// - /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path. - /// - /// - /// - /// - /// When used with a Virtual-Directory set-up, this would resolve all URLs correctly. - /// The recommendation is that the "ResolveUrlsFromTextString" option (in umbracoSettings.config) is set to false for non-Virtual-Directory installs. - /// - public static string ResolveUrlsFromTextString(string text) - { + /// + /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path. + /// + /// + /// + /// + /// When used with a Virtual-Directory set-up, this would resolve all URLs correctly. + /// The recommendation is that the "ResolveUrlsFromTextString" option (in umbracoSettings.config) is set to false for non-Virtual-Directory installs. + /// + public static string ResolveUrlsFromTextString(string text) + { if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString == false) return text; using (var timer = Current.ProfilingLogger.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) - { - // find all relative urls (ie. urls that contain ~) - var tags = ResolveUrlPattern.Matches(text); + { + // find all relative urls (ie. urls that contain ~) + var tags = ResolveUrlPattern.Matches(text); Current.Logger.Debug(typeof(IOHelper), "After regex: " + timer.Stopwatch.ElapsedMilliseconds + " matched: " + tags.Count); - foreach (Match tag in tags) - { - var url = ""; - if (tag.Groups[1].Success) - url = tag.Groups[1].Value; + foreach (Match tag in tags) + { + var url = ""; + if (tag.Groups[1].Success) + url = tag.Groups[1].Value; - // The richtext editor inserts a slash in front of the url. That's why we need this little fix - // if (url.StartsWith("/")) - // text = text.Replace(url, ResolveUrl(url.Substring(1))); - // else - if (String.IsNullOrEmpty(url) == false) - { - var resolvedUrl = (url.Substring(0, 1) == "/") ? IOHelper.ResolveUrl(url.Substring(1)) : IOHelper.ResolveUrl(url); - text = text.Replace(url, resolvedUrl); - } - } - } + // The richtext editor inserts a slash in front of the url. That's why we need this little fix + // if (url.StartsWith("/")) + // text = text.Replace(url, ResolveUrl(url.Substring(1))); + // else + if (String.IsNullOrEmpty(url) == false) + { + var resolvedUrl = (url.Substring(0, 1) == "/") ? IOHelper.ResolveUrl(url.Substring(1)) : IOHelper.ResolveUrl(url); + text = text.Replace(url, resolvedUrl); + } + } + } - return text; - } + return text; + } - } + public static string CleanForXss(string text, params char[] ignoreFromClean) + { + return text.CleanForXss(ignoreFromClean); + } + } } diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs index e960705495..c980958b78 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs @@ -263,7 +263,7 @@ namespace Umbraco.Web.Trees return Attempt.Succeed( new LegacyUrlAction( "dialogs/republish.aspx?rnd=" + DateTime.UtcNow.Ticks, - "Republishing entire site")); + Current.Services.TextService.Localize("actions/republish"))); case "UmbClientMgr.appActions().actionAssignDomain()": return Attempt.Succeed( new LegacyUrlAction( @@ -424,4 +424,4 @@ namespace Umbraco.Web.Trees } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 1bfb4935e7..681a385056 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -172,6 +172,7 @@ + @@ -1740,11 +1741,13 @@ umbraco_org_umbraco_update_CheckForUpgrade + 11.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v15.0 diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs index 27eadf265b..e25fe4763b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs @@ -30,7 +30,7 @@ namespace umbraco.dialogs protected void Page_Load(object sender, EventArgs e) { Button1.Text = Services.TextService.Localize("update"); - pane_form.Text = "Set permissions for the page " + _node.Text; + pane_form.Text = Services.TextService.Localize("actions/SetPermissionsForThePage",_node.Text); } override protected void OnInit(EventArgs e) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index e562e38497..67033e42f7 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -28,28 +28,31 @@ namespace UmbracoExamine /// public class UmbracoContentIndexer : BaseUmbracoIndexer { - protected IContentService ContentService { get; private set; } - protected IMediaService MediaService { get; private set; } - protected IUserService UserService { get; private set; } + protected IContentService ContentService { get; } + protected IMediaService MediaService { get; } + protected IUserService UserService { get; } + private readonly IEnumerable _urlSegmentProviders; private readonly IQueryFactory _queryFactory; private int? _parentId; #region Constructors - /// - /// Default constructor - /// + // default - bad, should inject instead + // usage: none public UmbracoContentIndexer() - : base() { ContentService = Current.Services.ContentService; MediaService = Current.Services.MediaService; UserService = Current.Services.UserService; + _urlSegmentProviders = Current.UrlSegmentProviders; _queryFactory = Current.DatabaseContext.QueryFactory; + + InitializeQueries(); } + // usage: IndexInitializer (tests) public UmbracoContentIndexer( IEnumerable fieldDefinitions, Directory luceneDirectory, @@ -66,12 +69,12 @@ namespace UmbracoExamine IDictionary> indexValueTypes = null) : base(fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, facetConfiguration, indexValueTypes) { - if (contentService == null) throw new ArgumentNullException("contentService"); - if (mediaService == null) throw new ArgumentNullException("mediaService"); - if (userService == null) throw new ArgumentNullException("userService"); - if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders"); - if (validator == null) throw new ArgumentNullException("validator"); - if (options == null) throw new ArgumentNullException("options"); + if (contentService == null) throw new ArgumentNullException(nameof(contentService)); + if (mediaService == null) throw new ArgumentNullException(nameof(mediaService)); + if (userService == null) throw new ArgumentNullException(nameof(userService)); + if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders)); + if (validator == null) throw new ArgumentNullException(nameof(validator)); + if (options == null) throw new ArgumentNullException(nameof(options)); if (queryFactory == null) throw new ArgumentNullException(nameof(queryFactory)); SupportProtectedContent = options.SupportProtectedContent; @@ -87,8 +90,15 @@ namespace UmbracoExamine UserService = userService; _urlSegmentProviders = urlSegmentProviders; _queryFactory = queryFactory; + + InitializeQueries(); } + private void InitializeQueries() + { + if (_publishedQuery == null) + _publishedQuery = _queryFactory.Create().Where(x => x.Published); + } #endregion @@ -131,7 +141,7 @@ namespace UmbracoExamine SupportProtectedContent = supportProtected; else SupportProtectedContent = false; - + base.Initialize(name, config); } @@ -168,11 +178,8 @@ namespace UmbracoExamine #endregion - #region Public methods - - /// /// Deletes a node from the index. /// @@ -209,6 +216,11 @@ namespace UmbracoExamine #region Protected + /// + /// This is a static query, it's parameters don't change so store statically + /// + private static IQuery _publishedQuery; + protected override void PerformIndexAll(string type) { const int pageSize = 10000; @@ -217,7 +229,6 @@ namespace UmbracoExamine switch (type) { case IndexTypes.Content: - var contentParentId = -1; if (ParentId.HasValue && ParentId.Value > 0) { @@ -237,9 +248,7 @@ namespace UmbracoExamine else { //add the published filter - var qry = _queryFactory.Create().Where(x => x.Published == true); - - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "Path", Direction.Ascending, true, qry); + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "Path", Direction.Ascending, true, _publishedQuery); } //if specific types are declared we need to post filter them @@ -256,19 +265,21 @@ namespace UmbracoExamine IndexItems(GetValueSets(content)); pageIndex++; - - } while (content.Length == pageSize); - break; case IndexTypes.Media: - var mediaParentId = -1; + if (ParentId.HasValue && ParentId.Value > 0) { mediaParentId = ParentId.Value; } + + // merge note: 7.5 changes this to use mediaService.GetPagedXmlEntries but we cannot merge the + // change here as mediaService does not provide access to Xml in v8 - and actually Examine should + // not assume that Umbraco provides Xml at all. + IMedia[] media; do @@ -321,10 +332,10 @@ namespace UmbracoExamine {"writerName", new object[] {c.GetWriterProfile(UserService).Name}}, {"writerID", new object[] {c.WriterId}}, {"version", new object[] {c.Version}}, - {"template", new object[] {c.Template == null ? 0 : c.Template.Id}} + {"template", new object[] {c.Template?.Id ?? 0}} }; - foreach (var property in c.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) + foreach (var property in c.Properties.Where(p => p?.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) { values.Add(property.Alias, new[] {property.Value}); } @@ -358,7 +369,7 @@ namespace UmbracoExamine {"creatorName", new object[] {m.GetCreatorProfile(UserService).Name}} }; - foreach (var property in m.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) + foreach (var property in m.Properties.Where(p => p?.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) { values.Add(property.Alias, new[] { property.Value }); } diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index 03d687a905..f21a1aa5b0 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -380,7 +380,7 @@ namespace umbraco.cms.businesslogic.packager if (languageItemsElement != null) { var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); - insPack.Data.Languages.AddRange(insertedLanguages.Select(l => l.Id.ToString())); + insPack.Data.Languages.AddRange(insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))); } #endregion @@ -390,24 +390,17 @@ namespace umbraco.cms.businesslogic.packager if (dictionaryItemsElement != null) { var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); - insPack.Data.DictionaryItems.AddRange(insertedDictionaryItems.Select(d => d.Id.ToString())); + insPack.Data.DictionaryItems.AddRange(insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))); } #endregion #region Macros - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//macro")) + var macroItemsElement = rootElement.Descendants("Macros").FirstOrDefault(); + if (macroItemsElement != null) { - //TODO: Fix this, this should not use the legacy API - Macro m = Macro.Import(n); - - if (m != null) - { - insPack.Data.Macros.Add(m.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; - } + var insertedMacros = packagingService.ImportMacros(macroItemsElement); + insPack.Data.Macros.AddRange(insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))); } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } #endregion #region Templates @@ -445,7 +438,7 @@ namespace umbraco.cms.businesslogic.packager { StyleSheet s = StyleSheet.Import(n, currentUser); - insPack.Data.Stylesheets.Add(s.Id.ToString()); + insPack.Data.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); //saveNeeded = true; } diff --git a/src/umbraco.cms/businesslogic/macro/Macro.cs b/src/umbraco.cms/businesslogic/macro/Macro.cs index 39a7367bc1..d441d4c00e 100644 --- a/src/umbraco.cms/businesslogic/macro/Macro.cs +++ b/src/umbraco.cms/businesslogic/macro/Macro.cs @@ -1,11 +1,8 @@ using System; -using System.Data; -using System.Globalization; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Xml; -using System.Runtime.CompilerServices; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.IO; @@ -177,22 +174,25 @@ namespace umbraco.cms.businesslogic.macro } } - /// - /// Macro initializer - /// - public Macro() + /// + /// Macro initializer + /// + [Obsolete("This should no longer be used, use the IMacroService and related models instead")] + public Macro() { } - /// - /// Macro initializer - /// - /// The id of the macro - public Macro(int Id) + /// + /// Macro initializer + /// + /// The id of the macro + [Obsolete("This should no longer be used, use the IMacroService and related models instead")] + public Macro(int Id) { Setup(Id); } + [Obsolete("This should no longer be used, use the IMacroService and related models instead")] internal Macro(IMacro macro) { MacroEntity = macro; @@ -202,15 +202,17 @@ namespace umbraco.cms.businesslogic.macro /// Initializes a new instance of the class. /// /// The alias. + [Obsolete("This should no longer be used, use the IMacroService and related models instead")] public Macro(string alias) { Setup(alias); } - /// - /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility - /// - public virtual void Save() + /// + /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility + /// + [Obsolete("This should no longer be used, use the IMacroService and related models instead")] + public virtual void Save() { Current.Services.MacroService.Save(MacroEntity); } @@ -223,8 +225,7 @@ namespace umbraco.cms.businesslogic.macro Current.Services.MacroService.Delete(MacroEntity); } - //TODO: Fix this, this should wrap a new API! - + [Obsolete("This is no longer used, use the IMacroService and related models instead")] public static Macro Import(XmlNode n) { var alias = XmlHelper.GetNodeValue(n.SelectSingleNode("alias")); diff --git a/src/umbraco.presentation.targets b/src/umbraco.presentation.targets index c38a1da485..2a33705d6f 100644 --- a/src/umbraco.presentation.targets +++ b/src/umbraco.presentation.targets @@ -55,6 +55,10 @@ $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll + + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll +