diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs index 62f81ee91d..ad4b180528 100644 --- a/src/Umbraco.Core/Macros/MacroTagParser.cs +++ b/src/Umbraco.Core/Macros/MacroTagParser.cs @@ -66,8 +66,7 @@ namespace Umbraco.Core.Macros } } - - var macroAlias = attributes["macroalias"] ?? attributes["alias"]; + var macroAlias = attributes.ContainsKey("macroalias") ? attributes["macroalias"] : attributes["alias"]; //call the callback now that we have the macro parsed macroFoundCallback(macroAlias, attributes); diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 9f83eaca5c..23fb4694e2 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -1,6 +1,14 @@ using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; using System.Linq; +using System.Web; +using System.Xml; using System.Xml.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; @@ -45,6 +53,298 @@ namespace Umbraco.Core.Models } } + /// + /// Sets and uploads the file from a HttpPostedFileBase object as the property value + /// + /// to add property value to + /// Alias of the property to save the value on + /// The containing the file that will be uploaded + public static void SetValue(this IMedia media, string propertyTypeAlias, HttpPostedFileBase value) + { + var name = + IOHelper.SafeFileName( + value.FileName.Substring(value.FileName.LastIndexOf(IOHelper.DirSepChar) + 1, + value.FileName.Length - value.FileName.LastIndexOf(IOHelper.DirSepChar) - 1) + .ToLower()); + + if(string.IsNullOrEmpty(name) == false) + SetFileOnContent(media, propertyTypeAlias, name, value.InputStream); + } + + /// + /// Sets and uploads the file from a HttpPostedFile object as the property value + /// + /// to add property value to + /// Alias of the property to save the value on + /// The containing the file that will be uploaded + public static void SetValue(this IMedia media, string propertyTypeAlias, HttpPostedFile value) + { + var name = + IOHelper.SafeFileName( + value.FileName.Substring(value.FileName.LastIndexOf(IOHelper.DirSepChar) + 1, + value.FileName.Length - value.FileName.LastIndexOf(IOHelper.DirSepChar) - 1) + .ToLower()); + + if (string.IsNullOrEmpty(name) == false) + SetFileOnContent(media, propertyTypeAlias, name, value.InputStream); + } + + /// + /// Sets and uploads the file from a HttpPostedFileWrapper object as the property value + /// + /// to add property value to + /// Alias of the property to save the value on + /// The containing the file that will be uploaded + public static void SetValue(this IMedia media, string propertyTypeAlias, HttpPostedFileWrapper value) + { + if (string.IsNullOrEmpty(value.FileName) == false) + SetFileOnContent(media, propertyTypeAlias, value.FileName, value.InputStream); + } + + /// + /// Sets and uploads the file from a HttpPostedFileBase object as the property value + /// + /// to add property value to + /// Alias of the property to save the value on + /// The containing the file that will be uploaded + public static void SetValue(this IContent content, string propertyTypeAlias, HttpPostedFileBase value) + { + var name = + IOHelper.SafeFileName( + value.FileName.Substring(value.FileName.LastIndexOf(IOHelper.DirSepChar) + 1, + value.FileName.Length - value.FileName.LastIndexOf(IOHelper.DirSepChar) - 1) + .ToLower()); + + if (string.IsNullOrEmpty(name) == false) + SetFileOnContent(content, propertyTypeAlias, name, value.InputStream); + } + + /// + /// Sets and uploads the file from a HttpPostedFile object as the property value + /// + /// to add property value to + /// Alias of the property to save the value on + /// The containing the file that will be uploaded + public static void SetValue(this IContent content, string propertyTypeAlias, HttpPostedFile value) + { + var name = + IOHelper.SafeFileName( + value.FileName.Substring(value.FileName.LastIndexOf(IOHelper.DirSepChar) + 1, + value.FileName.Length - value.FileName.LastIndexOf(IOHelper.DirSepChar) - 1) + .ToLower()); + + if (string.IsNullOrEmpty(name) == false) + SetFileOnContent(content, propertyTypeAlias, name, value.InputStream); + } + + /// + /// Sets and uploads the file from a HttpPostedFileWrapper object as the property value + /// + /// to add property value to + /// Alias of the property to save the value on + /// The containing the file that will be uploaded + public static void SetValue(this IContent content, string propertyTypeAlias, HttpPostedFileWrapper value) + { + if (string.IsNullOrEmpty(value.FileName) == false) + SetFileOnContent(content, propertyTypeAlias, value.FileName, value.InputStream); + } + + private static void SetFileOnContent(IContentBase content, string propertyTypeAlias, string name, Stream fileStream) + { + var property = content.Properties.FirstOrDefault(x => x.Alias == propertyTypeAlias); + if(property == null) + return; + + bool supportsResizing = false; + string fileName = UmbracoSettings.UploadAllowDirectories + ? Path.Combine(property.Id.ToString(), name) + : property.Id + "-" + name; + string extension = Path.GetExtension(name); + + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + fs.AddFile(fileName, fileStream); + + //Check if file supports resizing and create thumbnails + if (("," + UmbracoSettings.ImageFileTypes + ",").Contains(string.Format(",{0},", extension))) + { + supportsResizing = true; + + // Make default thumbnail + var thumbUrl = Resize(fs, fileName, extension, 100, "thumb"); + + //Look up Prevalues for this upload datatype - if it is an upload datatype + var uploadFieldId = new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c"); + if (property.PropertyType.DataTypeControlId == uploadFieldId) + { + //Get Prevalues by the DataType's Id: property.PropertyType.DataTypeId + var values = ApplicationContext.Current.Services.DataTypeService.GetPreValuesByDataTypeId(property.PropertyType.DataTypeId); + var thumbnailSizes = values.FirstOrDefault(); + //Additional thumbnails configured as prevalues on the DataType + if (thumbnailSizes != null) + { + char sep = (!thumbnailSizes.Contains("") && thumbnailSizes.Contains(",")) ? ',' : ';'; + + foreach (string thumb in thumbnailSizes.Split(sep)) + { + int thumbSize; + if (thumb != "" && int.TryParse(thumb, out thumbSize)) + { + Resize(fs, fileName, extension, thumbSize, string.Format("thumb_{0}", thumbSize)); + } + } + } + } + } + + //Check for auto fill of additional properties + if (UmbracoSettings.ImageAutoFillImageProperties != null) + { + XmlNode uploadFieldConfigNode = + UmbracoSettings.ImageAutoFillImageProperties.SelectSingleNode( + string.Format("uploadField [@alias = \"{0}\"]", propertyTypeAlias)); + + if (uploadFieldConfigNode != null) + { + //Only add dimensions to web images + if (supportsResizing) + { + SetPropertyValue(content, uploadFieldConfigNode, "widthFieldAlias", GetDimensions(fs, fileName).Item1); + SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", GetDimensions(fs, fileName).Item2); + } + else + { + SetPropertyValue(content, uploadFieldConfigNode, "widthFieldAlias", string.Empty); + SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", string.Empty); + } + + SetPropertyValue(content, uploadFieldConfigNode, "lengthFieldAlias", fs.GetSize(fileName)); + SetPropertyValue(content, uploadFieldConfigNode, "extensionFieldAlias", extension); + } + } + + //Set the value of the property to that of the uploaded file's url + property.Value = fs.GetUrl(fileName); + } + + private static void SetPropertyValue(IContentBase content, XmlNode uploadFieldConfigNode, string propertyAlias, object propertyValue) + { + XmlNode propertyNode = uploadFieldConfigNode.SelectSingleNode(propertyAlias); + if (propertyNode != null && string.IsNullOrEmpty(propertyNode.FirstChild.Value) == false) + { + content.SetValue(propertyNode.FirstChild.Value, propertyValue); + } + } + + private static string Resize(MediaFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition) + { + var fileNameThumb = DoResize(fileSystem, path, extension, GetDimensions(fileSystem, path).Item1, GetDimensions(fileSystem, path).Item2, maxWidthHeight, fileNameAddition); + + return fileSystem.GetUrl(fileNameThumb); + } + + private static Tuple GetDimensions(MediaFileSystem fileSystem, string path) + { + var fs = fileSystem.OpenFile(path); + var image = Image.FromStream(fs); + var fileWidth = image.Width; + var fileHeight = image.Height; + fs.Close(); + image.Dispose(); + + return new Tuple(fileWidth, fileHeight); + } + + private static string DoResize(MediaFileSystem fileSystem, string path, string extension, int width, int height, int maxWidthHeight, string fileNameAddition) + { + var fs = fileSystem.OpenFile(path); + var image = Image.FromStream(fs); + fs.Close(); + + string fileNameThumb = String.IsNullOrEmpty(fileNameAddition) ? + string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf("."))) : + string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition); + + var thumb = GenerateThumbnail(fileSystem, + image, + maxWidthHeight, + width, + height, + path, + extension, + fileNameThumb, + maxWidthHeight == 0); + + fileNameThumb = thumb.Item3; + + image.Dispose(); + + return fileNameThumb; + } + + private static Tuple GenerateThumbnail(MediaFileSystem fileSystem, Image image, int maxWidthHeight, int fileWidth, + int fileHeight, string fullFilePath, string extension, + string thumbnailFileName, bool useFixedDimensions) + { + // Generate thumbnail + float f = 1; + if (!useFixedDimensions) + { + var fx = (float)image.Size.Width / (float)maxWidthHeight; + var fy = (float)image.Size.Height / (float)maxWidthHeight; + + // must fit in thumbnail size + f = Math.Max(fx, fy); //if (f < 1) f = 1; + } + + var widthTh = (int)Math.Round((float)fileWidth / f); int heightTh = (int)Math.Round((float)fileHeight / f); + + // fixes for empty width or height + if (widthTh == 0) + widthTh = 1; + if (heightTh == 0) + heightTh = 1; + + // Create new image with best quality settings + var bp = new Bitmap(widthTh, heightTh); + var g = Graphics.FromImage(bp); + g.SmoothingMode = SmoothingMode.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + + // Copy the old image to the new and resized + var rect = new Rectangle(0, 0, widthTh, heightTh); + g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel); + + // Copy metadata + var imageEncoders = ImageCodecInfo.GetImageEncoders(); + ImageCodecInfo codec = null; + if (extension.ToLower() == "png" || extension.ToLower() == "gif") + codec = imageEncoders.Single(t => t.MimeType.Equals("image/png")); + else + codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg")); + + + // Set compresion ratio to 90% + var ep = new EncoderParameters(); + ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L); + + // Save the new image using the dimensions of the image + string newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", + string.Format("{0}x{1}", widthTh, heightTh)); + var ms = new MemoryStream(); + bp.Save(ms, codec, ep); + ms.Seek(0, 0); + + fileSystem.AddFile(newFileName, ms); + + ms.Close(); + bp.Dispose(); + g.Dispose(); + + return new Tuple(widthTh, heightTh, newFileName); + } + /// /// Gets the for the Creator of this content. /// diff --git a/src/Umbraco.Core/Models/Media.cs b/src/Umbraco.Core/Models/Media.cs index ce95509d02..430d4cd1c6 100644 --- a/src/Umbraco.Core/Models/Media.cs +++ b/src/Umbraco.Core/Models/Media.cs @@ -121,15 +121,5 @@ namespace Umbraco.Core.Models if (Key == Guid.Empty) Key = Guid.NewGuid(); } - - /// - /// Method to call when Entity is being updated - /// - /// Modified Date is set and a new Version guid is set - internal override void UpdatingEntity() - { - base.UpdatingEntity(); - Version = Guid.NewGuid(); - } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/Member.cs b/src/Umbraco.Core/Models/Membership/Member.cs new file mode 100644 index 0000000000..e1d7224623 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/Member.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents an Umbraco Member + /// + /// + /// Should be internal until a proper user/membership implementation + /// is part of the roadmap. + /// + [Serializable] + [DataContract(IsReference = true)] + internal class Member : MemberProfile, IMembershipUser + { + public int Id { get; set; } + public string Username { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string PasswordQuestion { get; set; } + public string PasswordAnswer { get; set; } + public string Comments { get; set; } + public bool IsApproved { get; set; } + public bool IsOnline { get; set; } + public bool IsLockedOut { get; set; } + public DateTime LastLoginDate { get; set; } + public DateTime LastPasswordChangeDate { get; set; } + public DateTime LastLockoutDate { get; set; } + public object ProfileId { get; set; } + public IEnumerable Groups { get; set; } + public Guid Key { get; set; } + public DateTime CreateDate { get; set; } + public DateTime UpdateDate { get; set; } + public bool HasIdentity { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/MemberGroup.cs b/src/Umbraco.Core/Models/Membership/MemberGroup.cs new file mode 100644 index 0000000000..2af92a0f12 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/MemberGroup.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents a Group for a Backoffice User + /// + /// + /// Should be internal until a proper user/membership implementation + /// is part of the roadmap. + /// + [Serializable] + [DataContract(IsReference = true)] + internal class MemberGroup : Entity + { + //Add MemberCollection ? + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/MemberProfile.cs b/src/Umbraco.Core/Models/Membership/MemberProfile.cs new file mode 100644 index 0000000000..aef6af3fd5 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/MemberProfile.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Models.Membership +{ + internal class MemberProfile : Profile + { + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/MemberType.cs b/src/Umbraco.Core/Models/Membership/MemberType.cs new file mode 100644 index 0000000000..d6baa03e68 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/MemberType.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents the Type for an Umbraco Member + /// + /// + /// Should be internal until a proper user/membership implementation + /// is part of the roadmap. + /// + [Serializable] + [DataContract(IsReference = true)] + internal class MemberType : Entity + { + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/Profile.cs b/src/Umbraco.Core/Models/Membership/Profile.cs index d77e5c1a6e..175448e61a 100644 --- a/src/Umbraco.Core/Models/Membership/Profile.cs +++ b/src/Umbraco.Core/Models/Membership/Profile.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Models.Membership /// [Serializable] [DataContract(IsReference = true)] - public class Profile : IProfile + internal class Profile : IProfile { /// /// Initializes a new instance of the class. diff --git a/src/Umbraco.Core/Models/Membership/UserGroup.cs b/src/Umbraco.Core/Models/Membership/UserGroup.cs new file mode 100644 index 0000000000..b0f10d9ff1 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/UserGroup.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents a Group for a Backoffice User + /// + /// + /// Should be internal until a proper user/membership implementation + /// is part of the roadmap. + /// + [Serializable] + [DataContract(IsReference = true)] + internal class UserGroup : Entity + { + //Add UserCollection ? + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UserType.cs b/src/Umbraco.Core/Models/Membership/UserType.cs index d424fad5e7..9a9083c88e 100644 --- a/src/Umbraco.Core/Models/Membership/UserType.cs +++ b/src/Umbraco.Core/Models/Membership/UserType.cs @@ -17,8 +17,10 @@ namespace Umbraco.Core.Models.Membership { [DataMember] public string Alias { get; set; } + [DataMember] public string Name { get; set; } + [DataMember] public string Permissions { get; set; } } diff --git a/src/Umbraco.Core/Persistence/Caching/InMemoryCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/InMemoryCacheProvider.cs index 799c35f2c2..2ef7b075af 100644 --- a/src/Umbraco.Core/Persistence/Caching/InMemoryCacheProvider.cs +++ b/src/Umbraco.Core/Persistence/Caching/InMemoryCacheProvider.cs @@ -78,7 +78,7 @@ namespace Umbraco.Core.Persistence.Caching /// public void Save(Type type, IEntity entity) { - _cache.AddOrUpdate(GetCompositeId(type, entity.Key), entity, (x, y) => entity); + _cache.AddOrUpdate(GetCompositeId(type, entity.Id), entity, (x, y) => entity); } /// @@ -89,7 +89,7 @@ namespace Umbraco.Core.Persistence.Caching public void Delete(Type type, IEntity entity) { IEntity entity1; - bool result = _cache.TryRemove(GetCompositeId(type, entity.Key), out entity1); + bool result = _cache.TryRemove(GetCompositeId(type, entity.Id), out entity1); } public void Clear() @@ -101,5 +101,10 @@ namespace Umbraco.Core.Persistence.Caching { return string.Format("{0}-{1}", type.Name, id.ToString()); } + + private string GetCompositeId(Type type, int id) + { + return string.Format("{0}-{1}", type.Name, id.ToGuid()); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs index a0b1171078..fa5d5e421e 100644 --- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs @@ -55,7 +55,7 @@ namespace Umbraco.Core.Persistence.Caching public void Save(Type type, IEntity entity) { - var key = GetCompositeId(type, entity.Key); + var key = GetCompositeId(type, entity.Id); var exists = _memoryCache.GetCacheItem(key) != null; _keyTracker.TryAdd(key, key); @@ -71,7 +71,7 @@ namespace Umbraco.Core.Persistence.Caching public void Delete(Type type, IEntity entity) { string throwaway = null; - var key = GetCompositeId(type, entity.Key); + var key = GetCompositeId(type, entity.Id); var keyBeSure = _keyTracker.TryGetValue(key, out throwaway); object itemRemoved = _memoryCache.Remove(key); _keyTracker.TryRemove(key, out throwaway); @@ -81,5 +81,10 @@ namespace Umbraco.Core.Persistence.Caching { return string.Format("{0}-{1}", type.Name, id.ToString()); } + + private string GetCompositeId(Type type, int id) + { + return string.Format("{0}-{1}", type.Name, id.ToGuid()); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs index b4f328a591..143a4b7fe6 100644 --- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs +++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs @@ -142,10 +142,13 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions IsUnique = attribute.IndexType == IndexTypes.UniqueNonClustered }; - var columns = attribute.ForColumns.Split(',').Select(p => p.Trim()); - foreach (var column in columns) + if (string.IsNullOrEmpty(attribute.ForColumns) == false) { - definition.Columns.Add(new IndexColumnDefinition{Name = column, Direction = Direction.Ascending}); + var columns = attribute.ForColumns.Split(',').Select(p => p.Trim()); + foreach (var column in columns) + { + definition.Columns.Add(new IndexColumnDefinition {Name = column, Direction = Direction.Ascending}); + } } return definition; } diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ForeignKeyDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ForeignKeyDefinition.cs index 4d6f1989f3..3012c57b0e 100644 --- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ForeignKeyDefinition.cs +++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ForeignKeyDefinition.cs @@ -9,6 +9,9 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions { ForeignColumns = new List(); PrimaryColumns = new List(); + //Set to None by Default + OnDelete = Rule.None; + OnUpdate = Rule.None; } public virtual string Name { get; set; } diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs index 8789274600..05110cab25 100644 --- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs @@ -8,12 +8,13 @@ namespace Umbraco.Core.Persistence /// /// /// If we are running in an http context - /// it will create on per context, otherwise it will a global singleton object which is NOT thread safe + /// it will create one per context, otherwise it will be a global singleton object which is NOT thread safe /// since we need (at least) a new instance of the database object per thread. /// internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory { - private readonly string _connectionString; + private readonly string _connectionStringName; + private readonly string _connectionString; private readonly string _providerName; private static volatile UmbracoDatabase _globalInstance = null; private static readonly object Locker = new object(); @@ -29,18 +30,18 @@ namespace Umbraco.Core.Persistence /// /// Constructor accepting custom connection string /// - /// - public DefaultDatabaseFactory(string connectionString) + /// Name of the connection string in web.config + public DefaultDatabaseFactory(string connectionStringName) { - Mandate.ParameterNotNullOrEmpty(connectionString, "connectionString"); - _connectionString = connectionString; + Mandate.ParameterNotNullOrEmpty(connectionStringName, "connectionStringName"); + _connectionStringName = connectionStringName; } /// /// Constructor accepting custom connectino string and provider name /// - /// - /// + /// Connection String to use with Database + /// Database Provider for the Connection String public DefaultDatabaseFactory(string connectionString, string providerName) { Mandate.ParameterNotNullOrEmpty(connectionString, "connectionString"); @@ -61,9 +62,9 @@ namespace Umbraco.Core.Persistence //double check if (_globalInstance == null) { - _globalInstance = string.IsNullOrEmpty(_providerName) - ? new UmbracoDatabase(_connectionString) - : new UmbracoDatabase(_connectionString, _providerName); + _globalInstance = string.IsNullOrEmpty(_providerName) == false && string.IsNullOrEmpty(_providerName) == false + ? new UmbracoDatabase(_connectionString, _providerName) + : new UmbracoDatabase(_connectionStringName); } } } @@ -74,9 +75,9 @@ namespace Umbraco.Core.Persistence if (!HttpContext.Current.Items.Contains(typeof(DefaultDatabaseFactory))) { HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory), - string.IsNullOrEmpty(_providerName) - ? new UmbracoDatabase(_connectionString) - : new UmbracoDatabase(_connectionString, _providerName)); + string.IsNullOrEmpty(_providerName) == false && string.IsNullOrEmpty(_providerName) == false + ? new UmbracoDatabase(_connectionString, _providerName) + : new UmbracoDatabase(_connectionStringName)); } return (UmbracoDatabase)HttpContext.Current.Items[typeof(DefaultDatabaseFactory)]; } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs index 7df3463dea..ba13bbcbfe 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs @@ -1,4 +1,5 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions { @@ -15,8 +16,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions public override string ToString() { - //TODO Implement usage of the SqlSyntax provider here to generate the sql statement for this expression. - return TableName + " " + Column.Name + " " + Column.Type ?? Column.CustomType; + return string.Format(SyntaxConfig.SqlSyntaxProvider.AlterColumn, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(Column.Name)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs index a310f850b8..c4c28119ea 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs @@ -1,3 +1,5 @@ +using Umbraco.Core.Persistence.SqlSyntax; + namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions { public class AlterDefaultConstraintExpression : IMigrationExpression @@ -5,16 +7,16 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions public virtual string SchemaName { get; set; } public virtual string TableName { get; set; } public virtual string ColumnName { get; set; } + public virtual string ConstraintName { get; set; } public virtual object DefaultValue { get; set; } public override string ToString() { - return base.ToString() + - string.Format("{0}.{1} {2} {3}", - SchemaName, - TableName, - ColumnName, - DefaultValue); + //NOTE Should probably investigate if Deleting a Default Constraint is different from deleting a 'regular' constraint + + return string.Format(SyntaxConfig.SqlSyntaxProvider.DeleteConstraint, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + SyntaxConfig.SqlSyntaxProvider.GetQuotedName(ConstraintName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs index bcff2fbc25..b9482e07e9 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs @@ -11,7 +11,7 @@ public override string ToString() { - return base.ToString() + TableName; + return string.Format("ALTER TABLE {0}", TableName); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs index 32a69e7591..e2d1b0c6e6 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs @@ -1,4 +1,6 @@ -using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using System.Linq; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions { @@ -13,8 +15,20 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions public override string ToString() { - //TODO replace with sql syntax provider - return base.ToString() + Constraint.ConstraintName; + var constraintType = (Constraint.IsPrimaryKeyConstraint) ? "PRIMARY KEY" : "UNIQUE"; + + string[] columns = new string[Constraint.Columns.Count]; + + for (int i = 0; i < Constraint.Columns.Count; i++) + { + columns[i] = SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(Constraint.Columns.ElementAt(i)); + } + + return string.Format(SyntaxConfig.SqlSyntaxProvider.CreateConstraint, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(Constraint.TableName), + SyntaxConfig.SqlSyntaxProvider.GetQuotedName(Constraint.ConstraintName), + constraintType, + string.Join(", ", columns)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs index 69c057fd74..8400eeca42 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions { @@ -16,8 +17,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions public override string ToString() { - //TODO replace with sql syntax provider - return base.ToString() + TableName; + return string.Format(SyntaxConfig.SqlSyntaxProvider.CreateTable, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + SyntaxConfig.SqlSyntaxProvider.Format(Columns)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs index cf11fc7119..a8ec8f3edc 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Linq; +using System.Text; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { @@ -16,8 +18,16 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions public override string ToString() { - //TODO Change to use sql syntax provider - return base.ToString() + TableName + " " + ColumnNames.Aggregate((a, b) => a + ", " + b); + var sb = new StringBuilder(); + foreach (string columnName in ColumnNames) + { + if (ColumnNames.First() != columnName) sb.AppendLine(";"); + sb.AppendFormat(SyntaxConfig.SqlSyntaxProvider.DropColumn, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(columnName)); + } + + return sb.ToString(); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs index ff449d306c..7833f92ac4 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs @@ -1,4 +1,5 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { @@ -13,8 +14,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions public override string ToString() { - //TODO change to use sql syntax provider - return base.ToString() + Constraint.ConstraintName; + return string.Format(SyntaxConfig.SqlSyntaxProvider.DeleteConstraint, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(Constraint.TableName), + SyntaxConfig.SqlSyntaxProvider.GetQuotedName(Constraint.ConstraintName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs index aa61e8cd6e..a37abf0650 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { @@ -14,5 +16,35 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { get { return _rows; } } + + public override string ToString() + { + var deleteItems = new List(); + + if (IsAllRows) + { + deleteItems.Add(string.Format(SyntaxConfig.SqlSyntaxProvider.DeleteData, SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), "1 = 1")); + } + else + { + foreach (var row in Rows) + { + var whereClauses = new List(); + foreach (KeyValuePair item in row) + { + whereClauses.Add(string.Format("{0} {1} {2}", + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(item.Key), + item.Value == null ? "IS" : "=", + SyntaxConfig.SqlSyntaxProvider.GetQuotedValue(item.Value.ToString()))); + } + + deleteItems.Add(string.Format(SyntaxConfig.SqlSyntaxProvider.DeleteData, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + String.Join(" AND ", whereClauses.ToArray()))); + } + } + + return String.Join("; ", deleteItems.ToArray()); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs index 55ee11ca3c..c84d5922da 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteDefaultConstraintExpression : IMigrationExpression { @@ -8,12 +10,9 @@ public override string ToString() { - //TODO Change to use sql syntax provider - return base.ToString() + - string.Format("{0}.{1} {2}", - SchemaName, - TableName, - ColumnName); + return string.Format(SyntaxConfig.SqlSyntaxProvider.DeleteDefaultConstraint, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(ColumnName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs index 3136beefab..8fb4f9e0f7 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs @@ -1,5 +1,6 @@ -using System.Linq; +using System; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { @@ -14,10 +15,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions public override string ToString() { - //TODO Change to use sql syntax provider - return base.ToString() + ForeignKey.Name + " " - + ForeignKey.ForeignTable + " (" + string.Join(", ", ForeignKey.ForeignColumns.ToArray()) + ") " - + ForeignKey.PrimaryTable + " (" + string.Join(", ", ForeignKey.PrimaryColumns.ToArray()) + ")"; + if (ForeignKey.ForeignTable == null) + throw new ArgumentNullException("Table name not specified, ensure you have appended the OnTable extension. Format should be Delete.ForeignKey(KeyName).OnTable(TableName)"); + + return string.Format(SyntaxConfig.SqlSyntaxProvider.DeleteConstraint, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(ForeignKey.ForeignTable), + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(ForeignKey.Name)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs index ee9cff2807..38fe9c3563 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs @@ -1,5 +1,5 @@ -using System.Linq; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { @@ -14,8 +14,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions public override string ToString() { - //TODO Change to use sql syntax provider - return base.ToString() + Index.TableName + " (" + string.Join(", ", Index.Columns.Select(x => x.Name).ToArray()) + ")"; + return string.Format(SyntaxConfig.SqlSyntaxProvider.DropIndex, + SyntaxConfig.SqlSyntaxProvider.GetQuotedName(Index.Name), + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(Index.TableName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs index 67f2418ec5..017244060f 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteTableExpression : IMigrationExpression { @@ -7,9 +9,8 @@ public override string ToString() { - //TODO implement the use of sql syntax provider - - return base.ToString() + TableName; + return string.Format(SyntaxConfig.SqlSyntaxProvider.DropTable, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs index 5980bbe75d..31fa681340 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs @@ -1,5 +1,4 @@ -using System; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions @@ -18,10 +17,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions public override string ToString() { - var output = String.Format(SyntaxConfig.SqlSyntaxProvider.AddColumn, - SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), - SyntaxConfig.SqlSyntaxProvider.Format(Column)); - return output; + return string.Format(SyntaxConfig.SqlSyntaxProvider.AddColumn, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + SyntaxConfig.SqlSyntaxProvider.Format(Column)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs index 7dafffcfbd..c9666654b0 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs @@ -1,5 +1,5 @@ -using System.Linq; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions { @@ -14,13 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions public override string ToString() { - return base.ToString() + - string.Format("{0} {1}({2}) {3}({4})", - ForeignKey.Name, - ForeignKey.ForeignTable, - string.Join(", ", ForeignKey.ForeignColumns.ToArray()), - ForeignKey.PrimaryTable, - string.Join(", ", ForeignKey.PrimaryColumns.ToArray())); + return SyntaxConfig.SqlSyntaxProvider.Format(ForeignKey); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs index 3b55c655c4..d15d1a0d97 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs @@ -1,5 +1,5 @@ -using System.Linq; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions { @@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions public override string ToString() { - return base.ToString() + Index.TableName + " (" + string.Join(", ", Index.Columns.Select(x => x.Name).ToArray()) + ")"; + return SyntaxConfig.SqlSyntaxProvider.Format(Index); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs index 62edf37851..795b65c0ef 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions { public class RenameColumnExpression : IMigrationExpression { @@ -9,8 +11,7 @@ public override string ToString() { - //TODO Implement usage of sql syntax provider - return base.ToString() + TableName + " " + OldName + " to " + NewName; + return SyntaxConfig.SqlSyntaxProvider.FormatColumnRename(TableName, OldName, NewName); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs index 28e733f949..3e3c5d8aee 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions { public class RenameTableExpression : IMigrationExpression { @@ -8,8 +10,7 @@ public override string ToString() { - //TODO Implement usage of sql syntax provider - return base.ToString() + OldName + " " + NewName; + return SyntaxConfig.SqlSyntaxProvider.FormatTableRename(OldName, NewName); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs index 06e78f7106..cac88440e8 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions { @@ -10,5 +11,37 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions public List> Set { get; set; } public List> Where { get; set; } public bool IsAllRows { get; set; } + + public override string ToString() + { + var updateItems = new List(); + var whereClauses = new List(); + + foreach (var item in Set) + { + updateItems.Add(string.Format("{0} = {1}", + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(item.Key), + SyntaxConfig.SqlSyntaxProvider.GetQuotedValue(item.Value.ToString()))); + } + + if (IsAllRows) + { + whereClauses.Add("1 = 1"); + } + else + { + foreach (var item in Where) + { + whereClauses.Add(string.Format("{0} {1} {2}", + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(item.Key), + item.Value == null ? "IS" : "=", + SyntaxConfig.SqlSyntaxProvider.GetQuotedValue(item.Value.ToString()))); + } + } + return string.Format(SyntaxConfig.SqlSyntaxProvider.UpdateData, + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(TableName), + string.Join(", ", updateItems.ToArray()), + string.Join(" AND ", whereClauses.ToArray())); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs index e154cb3fbc..33e6bd76e9 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs @@ -1,8 +1,9 @@ using System; +using System.Linq; using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Migrations.Initial; using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions; namespace Umbraco.Core.Persistence { @@ -28,15 +29,13 @@ namespace Umbraco.Core.Persistence public static void CreateTable(this Database db, bool overwrite, Type modelType) { - //TODO The line below should be refactored to use 'Umbraco.Core.Persistence.DatabaseModelDefinitions.DefinitionFactory.GetTableDefinition(modelType)' - //But first the sql syntax provider should be updated/refactored to format sql statements using the 'new' definitions from the DatabaseModelDefinitions-namespace. var tableDefinition = DefinitionFactory.GetTableDefinition(modelType); - var tableName = tableDefinition.TableName; + var tableName = tableDefinition.Name; - string createSql = SyntaxConfig.SqlSyntaxProvider.ToCreateTableStatement(tableDefinition); - string createPrimaryKeySql = SyntaxConfig.SqlSyntaxProvider.ToCreatePrimaryKeyStatement(tableDefinition); - var foreignSql = SyntaxConfig.SqlSyntaxProvider.ToCreateForeignKeyStatements(tableDefinition); - var indexSql = SyntaxConfig.SqlSyntaxProvider.ToCreateIndexStatements(tableDefinition); + string createSql = SyntaxConfig.SqlSyntaxProvider.Format(tableDefinition); + string createPrimaryKeySql = SyntaxConfig.SqlSyntaxProvider.FormatPrimaryKey(tableDefinition); + var foreignSql = SyntaxConfig.SqlSyntaxProvider.Format(tableDefinition.ForeignKeys); + var indexSql = SyntaxConfig.SqlSyntaxProvider.Format(tableDefinition.Indexes); var tableExist = db.TableExist(tableName); if (overwrite && tableExist) @@ -50,10 +49,14 @@ namespace Umbraco.Core.Persistence { //Execute the Create Table sql int created = db.Execute(new Sql(createSql)); + LogHelper.Info(string.Format("Create Table sql {0}:\n {1}", created, createSql)); //If any statements exists for the primary key execute them here if (!string.IsNullOrEmpty(createPrimaryKeySql)) - db.Execute(new Sql(createPrimaryKeySql)); + { + int createdPk = db.Execute(new Sql(createPrimaryKeySql)); + LogHelper.Info(string.Format("Primary Key sql {0}:\n {1}", createdPk, createPrimaryKeySql)); + } //Fires the NewTable event, which is used internally to insert base data before adding constrants to the schema if (NewTable != null) @@ -61,14 +64,14 @@ namespace Umbraco.Core.Persistence var e = new TableCreationEventArgs(); //Turn on identity insert if db provider is not mysql - if (ApplicationContext.Current.DatabaseContext.ProviderName.Contains("MySql") == false && tableDefinition.IsIdentity) + if (ApplicationContext.Current.DatabaseContext.ProviderName.Contains("MySql") == false && tableDefinition.Columns.Any(x => x.IsIdentity)) db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(tableName)))); //Call the NewTable-event to trigger the insert of base/default data NewTable(tableName, db, e); //Turn off identity insert if db provider is not mysql - if (ApplicationContext.Current.DatabaseContext.ProviderName.Contains("MySql") == false && tableDefinition.IsIdentity) + if (ApplicationContext.Current.DatabaseContext.ProviderName.Contains("MySql") == false && tableDefinition.Columns.Any(x => x.IsIdentity)) db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF;", SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(tableName)))); } @@ -76,23 +79,25 @@ namespace Umbraco.Core.Persistence foreach (var sql in foreignSql) { int createdFk = db.Execute(new Sql(sql)); + LogHelper.Info(string.Format("Create Foreign Key sql {0}:\n {1}", createdFk, sql)); } //Loop through index statements and execute sql foreach (var sql in indexSql) { int createdIndex = db.Execute(new Sql(sql)); + LogHelper.Info(string.Format("Create Index sql {0}:\n {1}", createdIndex, sql)); } //Specific to Sql Ce - look for changes to Identity Seed - if (ApplicationContext.Current.DatabaseContext.ProviderName.Contains("SqlServerCe")) - { - var seedSql = SyntaxConfig.SqlSyntaxProvider.ToAlterIdentitySeedStatements(tableDefinition); - foreach (var sql in seedSql) - { - int createdSeed = db.Execute(new Sql(sql)); - } - } + //if (ApplicationContext.Current.DatabaseContext.ProviderName.Contains("SqlServerCe")) + //{ + // var seedSql = SyntaxConfig.SqlSyntaxProvider.ToAlterIdentitySeedStatements(tableDefinition); + // foreach (var sql in seedSql) + // { + // int createdSeed = db.Execute(new Sql(sql)); + // } + //} transaction.Complete(); } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 9b0ebd8c62..efdafa91d8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// Represents a repository for doing CRUD operations for /// - internal class ContentRepository : PetaPocoRepositoryBase, IContentRepository + internal class ContentRepository : VersionableRepositoryBase, IContentRepository { private readonly IContentTypeRepository _contentTypeRepository; private readonly ITemplateRepository _templateRepository; @@ -40,11 +40,11 @@ namespace Umbraco.Core.Persistence.Repositories protected override IContent PerformGet(int id) { - var contentSql = GetBaseQuery(false); - contentSql.Where(GetBaseWhereClause(), new { Id = id }); - contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC"); + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + sql.OrderByDescending(x => x.VersionDate); - var dto = Database.Query(contentSql).FirstOrDefault(); + var dto = Database.Query(sql).FirstOrDefault(); if (dto == null) return null; @@ -153,6 +153,40 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + #region Overrides of VersionableRepositoryBase + + public override IContent GetByVersion(Guid versionId) + { + var sql = GetBaseQuery(false); + sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId }); + sql.OrderByDescending(x => x.VersionDate); + + var dto = Database.Query(sql).FirstOrDefault(); + + if (dto == null) + return null; + + var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); + + var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); + var content = factory.BuildEntity(dto); + + content.Properties = GetPropertyCollection(dto.NodeId, versionId, contentType); + + ((ICanBeDirty)content).ResetDirtyProperties(); + return content; + } + + protected override void PerformDeleteVersion(int id, Guid versionId) + { + Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE nodeId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + } + + #endregion + #region Unit of Work Implementation protected override void PersistNewItem(IContent entity) @@ -308,6 +342,8 @@ namespace Umbraco.Core.Persistence.Repositories //Create the PropertyData for this version - cmsPropertyData var propertyFactory = new PropertyFactory(((Content)entity).ContentType, entity.Version, entity.Id); var propertyDataDtos = propertyFactory.BuildDto(entity.Properties); + var keyDictionary = new Dictionary(); + //Add Properties foreach (var propertyDataDto in propertyDataDtos) { @@ -317,7 +353,17 @@ namespace Umbraco.Core.Persistence.Repositories } else { - Database.Insert(propertyDataDto); + int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto)); + keyDictionary.Add(propertyDataDto.PropertyTypeId, primaryKey); + } + } + + //Update Properties with its newly set Id + if (keyDictionary.Any()) + { + foreach (var property in entity.Properties) + { + property.Id = keyDictionary[property.PropertyTypeId]; } } @@ -358,97 +404,19 @@ namespace Umbraco.Core.Persistence.Repositories #region Implementation of IContentRepository - public IEnumerable GetAllVersions(int id) - { - var contentSql = GetBaseQuery(false); - contentSql.Where(GetBaseWhereClause(), new { Id = id }); - contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC"); - - var documentDtos = Database.Fetch(contentSql); - foreach (var dto in documentDtos) - { - yield return GetByVersion(id, dto.ContentVersionDto.VersionId); - } - } - - public IContent GetByVersion(int id, Guid versionId) - { - var contentSql = GetBaseQuery(false); - contentSql.Where(GetBaseWhereClause(), new { Id = id }); - contentSql.Where("[cmsContentVersion].[VersionId] = @VersionId", new { VersionId = versionId }); - contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC"); - - var dto = Database.Query(contentSql).FirstOrDefault(); - - if (dto == null) - return null; - - var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); - - var factory = new ContentFactory(contentType, NodeObjectTypeId, id); - var content = factory.BuildEntity(dto); - - content.Properties = GetPropertyCollection(id, versionId, contentType); - - ((ICanBeDirty)content).ResetDirtyProperties(); - return content; - } - public IContent GetByLanguage(int id, string language) { - var contentSql = GetBaseQuery(false); - contentSql.Where(GetBaseWhereClause(), new { Id = id }); - contentSql.Where("[cmsContentVersion].[LanguageLocale] = @Language", new { Language = language }); - contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC"); + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + sql.Where("[cmsContentVersion].[LanguageLocale] = @Language", new { Language = language }); + sql.OrderByDescending(x => x.VersionDate); - var dto = Database.Query(contentSql).FirstOrDefault(); + var dto = Database.Query(sql).FirstOrDefault(); if (dto == null) return null; - return GetByVersion(dto.NodeId, dto.ContentVersionDto.VersionId); - } - - public void Delete(int id, Guid versionId) - { - var documentDto = Database.FirstOrDefault("WHERE nodeId = @Id AND versionId = @VersionId AND newest = @Newest", new { Id = id, VersionId = versionId, Newest = false }); - Mandate.That(documentDto != null); - - using (var transaction = Database.GetTransaction()) - { - DeleteVersion(id, versionId); - - transaction.Complete(); - } - } - - public void Delete(int id, DateTime versionDate) - { - var list = Database.Fetch("WHERE nodeId = @Id AND VersionDate < @VersionDate", new { Id = id, VersionDate = versionDate }); - Mandate.That(list.Any()); - - using (var transaction = Database.GetTransaction()) - { - foreach (var dto in list) - { - DeleteVersion(id, dto.VersionId); - } - - transaction.Complete(); - } - } - - /// - /// Private method to execute the delete statements for removing a single version for a Content item. - /// - /// Id of the to delete a version from - /// Guid id of the version to delete - private void DeleteVersion(int id, Guid versionId) - { - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); - Database.Delete("WHERE nodeId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId }); - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + return GetByVersion(dto.ContentVersionDto.VersionId); } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index a483eea672..55c2c7b682 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -1,40 +1,9 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models; +using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { - public interface IContentRepository : IRepositoryQueryable + public interface IContentRepository : IRepositoryVersionable { - /// - /// Gets a list of all versions for an . - /// - /// Id of the to retrieve versions from - /// An enumerable list of the same object with different versions - IEnumerable GetAllVersions(int id); - - /// - /// Gets a specific version of an . - /// - /// Id of the to retrieve version from - /// Id of the version to retrieve - /// An item - IContent GetByVersion(int id, Guid versionId); - - /// - /// Deletes a specific version from an object. - /// - /// Id of the object to delete a version from - /// Id of the version to delete - void Delete(int id, Guid versionId); - - /// - /// Deletes versions from an object prior to a specific date. - /// - /// Id of the object to delete versions from - /// Latest version date - void Delete(int id, DateTime versionDate); - /// /// Gets a specific language version of an /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs index 354a8a7b1e..0b92f182d8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Persistence.Repositories { - public interface IMediaRepository : IRepositoryQueryable + public interface IMediaRepository : IRepositoryVersionable { } diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs new file mode 100644 index 0000000000..c8b6f79f7d --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Persistence.Repositories +{ + /// + /// Defines the implementation of a Repository, which allows getting versions of an + /// + /// Type of entity for which the repository is used + /// Type of the Id used for this entity + public interface IRepositoryVersionable : IRepositoryQueryable + where TEntity : IAggregateRoot + { + /// + /// Gets a list of all versions for an . + /// + /// Id of the to retrieve versions from + /// An enumerable list of the same object with different versions + IEnumerable GetAllVersions(int id); + + /// + /// Gets a specific version of an . + /// + /// Id of the version to retrieve + /// An item + TEntity GetByVersion(Guid versionId); + + /// + /// Deletes a specific version from an object. + /// + /// Id of the version to delete + void DeleteVersion(Guid versionId); + + /// + /// Deletes versions from an object prior to a specific date. + /// + /// Id of the object to delete versions from + /// Latest version date + void DeleteVersions(int id, DateTime versionDate); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index d021f7559b..288ccd43e5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// Represents a repository for doing CRUD operations for /// - internal class MediaRepository : PetaPocoRepositoryBase, IMediaRepository + internal class MediaRepository : VersionableRepositoryBase, IMediaRepository { private readonly IMediaTypeRepository _mediaTypeRepository; @@ -35,24 +35,24 @@ namespace Umbraco.Core.Persistence.Repositories protected override IMedia PerformGet(int id) { - var contentSql = GetBaseQuery(false); - contentSql.Where(GetBaseWhereClause(), new { Id = id }); - contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC"); + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + sql.OrderByDescending(x => x.VersionDate); - var dto = Database.Query(contentSql).FirstOrDefault(); + var dto = Database.Query(sql).FirstOrDefault(); if (dto == null) return null; - var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); + var mediaType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); - var factory = new MediaFactory(contentType, NodeObjectTypeId, id); - var content = factory.BuildEntity(dto); + var factory = new MediaFactory(mediaType, NodeObjectTypeId, id); + var media = factory.BuildEntity(dto); - content.Properties = GetPropertyCollection(id, dto.VersionId, contentType); + media.Properties = GetPropertyCollection(id, dto.VersionId, mediaType); - ((ICanBeDirty)content).ResetDirtyProperties(); - return content; + ((ICanBeDirty)media).ResetDirtyProperties(); + return media; } protected override IEnumerable PerformGetAll(params int[] ids) @@ -131,8 +131,41 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + #region Overrides of VersionableRepositoryBase + + public override IMedia GetByVersion(Guid versionId) + { + var sql = GetBaseQuery(false); + sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId }); + sql.OrderByDescending(x => x.VersionDate); + + var dto = Database.Query(sql).FirstOrDefault(); + + if (dto == null) + return null; + + var mediaType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); + + var factory = new MediaFactory(mediaType, NodeObjectTypeId, dto.NodeId); + var media = factory.BuildEntity(dto); + + media.Properties = GetPropertyCollection(dto.NodeId, dto.VersionId, mediaType); + + ((ICanBeDirty)media).ResetDirtyProperties(); + return media; + } + + protected override void PerformDeleteVersion(int id, Guid versionId) + { + Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE nodeId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId }); + } + + #endregion + #region Unit of Work Implementation - + protected override void PersistNewItem(IMedia entity) { ((Models.Media)entity).AddingEntity(); @@ -198,7 +231,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IMedia entity) { - //Updates Modified date and Version Guid + //Updates Modified date ((Models.Media)entity).UpdatingEntity(); var factory = new MediaFactory(NodeObjectTypeId, entity.Id); @@ -219,17 +252,36 @@ namespace Umbraco.Core.Persistence.Repositories Database.Update(newContentDto); } - //Create a new version - cmsContentVersion - //Assumes a new Version guid and Version date (modified date) has been set - Database.Insert(dto); + //Updates the current version - cmsContentVersion + //Assumes a Version guid exists and Version date (modified date) has been set/updated + Database.Update(dto); //Create the PropertyData for this version - cmsPropertyData var propertyFactory = new PropertyFactory(entity.ContentType, entity.Version, entity.Id); var propertyDataDtos = propertyFactory.BuildDto(entity.Properties); + var keyDictionary = new Dictionary(); + //Add Properties foreach (var propertyDataDto in propertyDataDtos) { - Database.Insert(propertyDataDto); + if (propertyDataDto.Id > 0) + { + Database.Update(propertyDataDto); + } + else + { + int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto)); + keyDictionary.Add(propertyDataDto.PropertyTypeId, primaryKey); + } + } + + //Update Properties with its newly set Id + if (keyDictionary.Any()) + { + foreach (var property in entity.Properties) + { + property.Id = keyDictionary[property.PropertyTypeId]; + } } ((ICanBeDirty)entity).ResetDirtyProperties(); diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs new file mode 100644 index 0000000000..57c79ef4f5 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + internal abstract class VersionableRepositoryBase : PetaPocoRepositoryBase + where TEntity : IAggregateRoot + { + protected VersionableRepositoryBase(IDatabaseUnitOfWork work) : base(work) + { + } + + protected VersionableRepositoryBase(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache) + { + } + + #region IRepositoryVersionable Implementation + + public virtual IEnumerable GetAllVersions(int id) + { + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + sql.OrderByDescending(x => x.VersionDate); + + var dtos = Database.Fetch(sql); + foreach (var dto in dtos) + { + yield return GetByVersion(dto.VersionId); + } + } + + public virtual void DeleteVersion(Guid versionId) + { + var dto = Database.FirstOrDefault("WHERE versionId = @VersionId AND newest = @Newest", new { VersionId = versionId, Newest = false }); + Mandate.That(dto != null); + + using (var transaction = Database.GetTransaction()) + { + PerformDeleteVersion(dto.NodeId, versionId); + + transaction.Complete(); + } + } + + public virtual void DeleteVersions(int id, DateTime versionDate) + { + var list = Database.Fetch("WHERE nodeId = @Id AND VersionDate < @VersionDate", new { Id = id, VersionDate = versionDate }); + Mandate.That(list.Any()); + + using (var transaction = Database.GetTransaction()) + { + foreach (var dto in list) + { + PerformDeleteVersion(id, dto.VersionId); + } + + transaction.Complete(); + } + } + + public abstract TEntity GetByVersion(Guid versionId); + + /// + /// Protected method to execute the delete statements for removing a single version for a TEntity item. + /// + /// Id of the to delete a version from + /// Guid id of the version to delete + protected abstract void PerformDeleteVersion(int id, Guid versionId); + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 6930f9d9d8..b59bcf3ec8 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -1,8 +1,6 @@ -using System; using System.Collections.Generic; -using System.Data; using Umbraco.Core.Persistence.DatabaseAnnotations; -using Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Persistence.SqlSyntax { @@ -15,17 +13,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax string GetQuotedColumnName(string columnName); string GetQuotedName(string name); bool DoesTableExist(Database db, string tableName); - string ToCreateTableStatement(TableDefinition tableDefinition); - List ToCreateForeignKeyStatements(TableDefinition tableDefinition); - List ToCreateIndexStatements(TableDefinition tableDefinition); - DbType GetColumnDbType(Type valueType); string GetIndexType(IndexTypes indexTypes); - string GetColumnDefinition(ColumnDefinition column, string tableName); - string GetPrimaryKeyStatement(ColumnDefinition column, string tableName); - string ToCreatePrimaryKeyStatement(TableDefinition table); string GetSpecialDbType(SpecialDbTypes dbTypes); - string GetConstraintDefinition(ColumnDefinition column, string tableName); - List ToAlterIdentitySeedStatements(TableDefinition table); string CreateTable { get; } string DropTable { get; } string AddColumn { get; } @@ -44,6 +33,17 @@ namespace Umbraco.Core.Persistence.SqlSyntax string CreateConstraint { get; } string DeleteConstraint { get; } string CreateForeignKeyConstraint { get; } - string Format(DatabaseModelDefinitions.ColumnDefinition column); + string DeleteDefaultConstraint { get; } + string Format(TableDefinition table); + string Format(IEnumerable columns); + List Format(IEnumerable indexes); + List Format(IEnumerable foreignKeys); + string FormatPrimaryKey(TableDefinition table); + string GetQuotedValue(string value); + string Format(ColumnDefinition column); + string Format(IndexDefinition index); + string Format(ForeignKeyDefinition foreignKey); + string FormatColumnRename(string tableName, string oldName, string newName); + string FormatTableRename(string oldName, string newName); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/ColumnDefinition.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/ColumnDefinition.cs deleted file mode 100644 index 71e1826d8e..0000000000 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/ColumnDefinition.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions -{ - public class ColumnDefinition - { - public string ColumnName { get; set; } - - public Type PropertyType { get; set; } - public bool HasSpecialDbType { get; set; } - public SpecialDbTypes DbType { get; set; } - public int? DbTypeLength { get; set; } - - public bool IsNullable { get; set; } - - public bool IsPrimaryKey { get; set; } - public bool IsPrimaryKeyIdentityColumn { get; set; } - public bool IsPrimaryKeyClustered { get; set; } - public string PrimaryKeyName { get; set; } - public string PrimaryKeyColumns { get; set; } - public int PrimaryKeySeeding { get; set; } - - public string ConstraintName { get; set; } - public string ConstraintDefaultValue { get; set; } - public bool HasConstraint - { - get { return !string.IsNullOrEmpty(ConstraintName) || !string.IsNullOrEmpty(ConstraintDefaultValue); } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/DefinitionFactory.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/DefinitionFactory.cs deleted file mode 100644 index 4e5666bbeb..0000000000 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/DefinitionFactory.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Linq; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions -{ - public static class DefinitionFactory - { - public static TableDefinition GetTableDefinition(Type modelType) - { - var tableNameAttribute = modelType.FirstAttribute(); - string tableName = tableNameAttribute.Value; - - var tableDefinition = new TableDefinition { TableName = tableName }; - var objProperties = modelType.GetProperties().ToList(); - foreach (var propertyInfo in objProperties) - { - //If current property has an IgnoreAttribute then skip it - var ignoreAttribute = propertyInfo.FirstAttribute(); - if (ignoreAttribute != null) continue; - - //If current property has a ResultColumnAttribute then skip it - var resultColumnAttribute = propertyInfo.FirstAttribute(); - if (resultColumnAttribute != null) continue; - - //Looks for ColumnAttribute with the name of the column, which would exist with ExplicitColumns - //Otherwise use the name of the property itself as the default convention - var columnAttribute = propertyInfo.FirstAttribute(); - string columnName = columnAttribute != null ? columnAttribute.Name : propertyInfo.Name; - - //Use PetaPoco's PrimaryKeyAttribute to set Identity property on TableDefinition - var primaryKeyAttribute = modelType.FirstAttribute(); - tableDefinition.IsIdentity = primaryKeyAttribute != null && primaryKeyAttribute.autoIncrement; - - //Creates a column definition and adds it to the collection on the table definition - var columnDefinition = new ColumnDefinition - { - ColumnName = columnName, - PropertyType = propertyInfo.PropertyType - }; - - //Look for specific DbType attributed a column - var databaseTypeAttribute = propertyInfo.FirstAttribute(); - if (databaseTypeAttribute != null) - { - columnDefinition.HasSpecialDbType = true; - columnDefinition.DbType = databaseTypeAttribute.DatabaseType; - } - - var lengthAttribute = propertyInfo.FirstAttribute(); - if(lengthAttribute != null) - { - columnDefinition.DbTypeLength = lengthAttribute.Length; - } - - //Look for specific Null setting attributed a column - var nullSettingAttribute = propertyInfo.FirstAttribute(); - if (nullSettingAttribute != null) - { - columnDefinition.IsNullable = nullSettingAttribute.NullSetting == NullSettings.Null; - } - - //Look for Primary Key for the current column - var primaryKeyColumnAttribute = propertyInfo.FirstAttribute(); - if (primaryKeyColumnAttribute != null) - { - columnDefinition.IsPrimaryKey = true; - columnDefinition.IsPrimaryKeyIdentityColumn = primaryKeyColumnAttribute.AutoIncrement; - columnDefinition.IsPrimaryKeyClustered = primaryKeyColumnAttribute.Clustered; - columnDefinition.PrimaryKeyName = primaryKeyColumnAttribute.Name ?? string.Empty; - columnDefinition.PrimaryKeyColumns = primaryKeyColumnAttribute.OnColumns ?? string.Empty; - columnDefinition.PrimaryKeySeeding = primaryKeyColumnAttribute.IdentitySeed; - } - - //Look for Constraint for the current column - var constraintAttribute = propertyInfo.FirstAttribute(); - if (constraintAttribute != null) - { - columnDefinition.ConstraintName = constraintAttribute.Name ?? string.Empty; - columnDefinition.ConstraintDefaultValue = constraintAttribute.Default ?? string.Empty; - } - - tableDefinition.ColumnDefinitions.Add(columnDefinition); - - //Creates a foreignkey definition and adds it to the collection on the table definition - var foreignKeyAttributes = propertyInfo.MultipleAttribute(); - if (foreignKeyAttributes != null) - { - foreach (var foreignKeyAttribute in foreignKeyAttributes) - { - var referencedTable = foreignKeyAttribute.Type.FirstAttribute(); - var referencedPrimaryKey = foreignKeyAttribute.Type.FirstAttribute(); - - string referencedColumn = string.IsNullOrEmpty(foreignKeyAttribute.Column) - ? referencedPrimaryKey.Value - : foreignKeyAttribute.Column; - - var foreignKeyDefinition = new ForeignKeyDefinition - { - ColumnName = columnName, - ConstraintName = foreignKeyAttribute.Name, - ReferencedColumnName = referencedColumn, - ReferencedTableName = referencedTable.Value - }; - tableDefinition.ForeignKeyDefinitions.Add(foreignKeyDefinition); - } - } - - //Creates an index definition and adds it to the collection on the table definition - var indexAttribute = propertyInfo.FirstAttribute(); - if (indexAttribute != null) - { - var indexDefinition = new IndexDefinition - { - ColumnNames = indexAttribute.ForColumns, - IndexName = indexAttribute.Name, - IndexType = indexAttribute.IndexType, - IndexForColumn = columnName - }; - tableDefinition.IndexDefinitions.Add(indexDefinition); - } - } - - return tableDefinition; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/ForeignKeyDefinition.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/ForeignKeyDefinition.cs deleted file mode 100644 index 06ce016134..0000000000 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/ForeignKeyDefinition.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions -{ - public class ForeignKeyDefinition - { - public string ConstraintName { get; set; } - public string ColumnName { get; set; } - public string ReferencedTableName { get; set; } - public string ReferencedColumnName { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/IndexDefinition.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/IndexDefinition.cs deleted file mode 100644 index 835a97e746..0000000000 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/IndexDefinition.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions -{ - public class IndexDefinition - { - public string IndexName { get; set; } - public IndexTypes IndexType { get; set; } - public string ColumnNames { get; set; } - public string IndexForColumn { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/TableDefinition.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/TableDefinition.cs deleted file mode 100644 index dfdacc1fbe..0000000000 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ModelDefinitions/TableDefinition.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions -{ - public class TableDefinition - { - public TableDefinition() - { - ColumnDefinitions = new List(); - ForeignKeyDefinitions = new List(); - IndexDefinitions = new List(); - } - - public string TableName { get; set; } - public bool IsIdentity { get; set; } - public bool IsPrimaryKeyClustered - { - get { return ColumnDefinitions.Any(x => x.IsPrimaryKeyClustered); } - } - public string PrimaryKeyName - { - get { return ColumnDefinitions.First(x => x.IsPrimaryKey).PrimaryKeyName ?? string.Empty; } - } - public string PrimaryKeyColumns - { - get { return ColumnDefinitions.First(x => x.IsPrimaryKey).PrimaryKeyColumns ?? string.Empty; } - } - - public List ColumnDefinitions { get; set; } - public List ForeignKeyDefinitions { get; set; } - public List IndexDefinitions { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs index 3c484a8ebf..a17b2339e3 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs @@ -1,9 +1,7 @@ -using System.Collections.Generic; -using System.Text; +using System; +using System.Linq; using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using ColumnDefinition = Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions.ColumnDefinition; -using TableDefinition = Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions.TableDefinition; namespace Umbraco.Core.Persistence.SqlSyntax { @@ -67,96 +65,20 @@ namespace Umbraco.Core.Persistence.SqlSyntax return "NVARCHAR"; } - public override string GetConstraintDefinition(ColumnDefinition column, string tableName) + public override string Format(IndexDefinition index) { - var sql = new StringBuilder(); + string name = string.IsNullOrEmpty(index.Name) + ? string.Format("IX_{0}_{1}", index.TableName, index.ColumnName) + : index.Name; - if (column.ConstraintDefaultValue.Equals("getdate()")) - { - sql.Append(" DEFAULT CURRENT_TIMESTAMP"); - } - else - { - sql.AppendFormat(DefaultValueFormat, column.ConstraintDefaultValue); - } + string columns = index.Columns.Any() + ? string.Join(",", index.Columns.Select(x => GetQuotedColumnName(x.Name))) + : GetQuotedColumnName(index.ColumnName); - return sql.ToString(); - } - - public override string GetColumnDefinition(ColumnDefinition column, string tableName) - { - string dbTypeDefinition; - if (column.HasSpecialDbType) - { - if (column.DbTypeLength.HasValue) - { - dbTypeDefinition = string.Format("{0}({1})", - GetSpecialDbType(column.DbType), - column.DbTypeLength.Value); - } - else - { - dbTypeDefinition = GetSpecialDbType(column.DbType); - } - } - else if (column.PropertyType == typeof(string)) - { - dbTypeDefinition = string.Format(StringLengthColumnDefinitionFormat, column.DbTypeLength.GetValueOrDefault(DefaultStringLength)); - } - else - { - if (!DbTypeMap.ColumnTypeMap.TryGetValue(column.PropertyType, out dbTypeDefinition)) - { - dbTypeDefinition = ""; - } - } - - var sql = new StringBuilder(); - sql.AppendFormat("{0} {1}", GetQuotedColumnName(column.ColumnName), dbTypeDefinition); - - if (column.IsPrimaryKeyIdentityColumn) - { - sql.Append(" NOT NULL PRIMARY KEY ").Append(AutoIncrementDefinition); - } - else - { - sql.Append(column.IsNullable ? " NULL" : " NOT NULL"); - } - - if (column.HasConstraint) - { - sql.Append(GetConstraintDefinition(column, tableName)); - sql = sql.Replace("DATETIME", "TIMESTAMP"); - } - - return sql.ToString(); - } - - public override string GetPrimaryKeyStatement(ColumnDefinition column, string tableName) - { - return string.Empty; - } - - public override List ToCreateIndexStatements(TableDefinition table) - { - var indexes = new List(); - - foreach (var index in table.IndexDefinitions) - { - string name = string.IsNullOrEmpty(index.IndexName) - ? string.Format("IX_{0}_{1}", table.TableName, index.IndexForColumn) - : index.IndexName; - - string columns = string.IsNullOrEmpty(index.ColumnNames) - ? GetQuotedColumnName(index.IndexForColumn) - : index.ColumnNames; - - indexes.Add(string.Format("CREATE INDEX {0} ON {1} ({2}); \n", - GetQuotedName(name), - GetQuotedTableName(table.TableName), - columns)); - } - return indexes; + return string.Format(CreateIndex, + GetQuotedName(name), + GetQuotedTableName(index.TableName), + columns); } public override bool DoesTableExist(Database db, string tableName) @@ -170,11 +92,40 @@ namespace Umbraco.Core.Persistence.SqlSyntax return result > 0; } - protected override string FormatIdentity(DatabaseModelDefinitions.ColumnDefinition column) + protected override string FormatIdentity(ColumnDefinition column) { return column.IsIdentity ? AutoIncrementDefinition : string.Empty; } + protected override string FormatDefaultValue(ColumnDefinition column) + { + if (column.DefaultValue == null) + return string.Empty; + + // see if this is for a system method + if (column.DefaultValue is SystemMethods) + { + string method = FormatSystemMethods((SystemMethods)column.DefaultValue); + if (string.IsNullOrEmpty(method)) + return string.Empty; + + return string.Format(DefaultValueFormat, method); + } + + if (column.DefaultValue.ToString().Equals("getdate()")) + return "DEFAULT CURRENT_TIMESTAMP"; + + return string.Format(DefaultValueFormat, column.DefaultValue); + } + + protected override string FormatPrimaryKey(ColumnDefinition column) + { + if(column.IsPrimaryKey) + return "PRIMARY KEY"; + + return string.Empty; + } + protected override string FormatSystemMethods(SystemMethods systemMethod) { switch (systemMethod) @@ -192,10 +143,22 @@ namespace Umbraco.Core.Persistence.SqlSyntax return null; } + public override string DeleteDefaultConstraint + { + get + { + throw new NotSupportedException("Default constraints are not supported in MySql"); + } + } + public override string AlterColumn { get { return "ALTER TABLE {0} MODIFY COLUMN {1}"; } } public override string DeleteConstraint { get { return "ALTER TABLE {0} DROP {1}{2}"; } } + public override string DropIndex { get { return "DROP INDEX {0} ON {1}"; } } + public override string CreateTable { get { return "CREATE TABLE {0} ({1}) ENGINE = INNODB"; } } + + public override string CreateIndex { get { return "CREATE INDEX {0} ON {1} ({2})"; } } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index 13f41903d7..6fb5a123c5 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -1,6 +1,6 @@ -using Umbraco.Core.Persistence.DatabaseAnnotations; +using System.Linq; +using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using ColumnDefinition = Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions.ColumnDefinition; namespace Umbraco.Core.Persistence.SqlSyntax { @@ -68,22 +68,25 @@ namespace Umbraco.Core.Persistence.SqlSyntax return string.Format("[{0}]", name); } - public override string GetPrimaryKeyStatement(ColumnDefinition column, string tableName) + public override string FormatPrimaryKey(TableDefinition table) { - string constraintName = string.IsNullOrEmpty(column.PrimaryKeyName) - ? string.Format("PK_{0}", tableName) - : column.PrimaryKeyName; + var columnDefinition = table.Columns.FirstOrDefault(x => x.IsPrimaryKey); + if (columnDefinition == null) + return string.Empty; - string columns = string.IsNullOrEmpty(column.PrimaryKeyColumns) - ? GetQuotedColumnName(column.ColumnName) - : column.PrimaryKeyColumns; + string constraintName = string.IsNullOrEmpty(columnDefinition.PrimaryKeyName) + ? string.Format("PK_{0}", table.Name) + : columnDefinition.PrimaryKeyName; - string sql = string.Format("ALTER TABLE {0} ADD CONSTRAINT {1} PRIMARY KEY ({2}); \n", - GetQuotedTableName(tableName), - GetQuotedName(constraintName), - columns); + string columns = string.IsNullOrEmpty(columnDefinition.PrimaryKeyColumns) + ? GetQuotedColumnName(columnDefinition.Name) + : columnDefinition.PrimaryKeyColumns; - return sql; + return string.Format(CreateConstraint, + GetQuotedTableName(table.Name), + GetQuotedName(constraintName), + "PRIMARY KEY", + columns); } public override bool DoesTableExist(Database db, string tableName) @@ -95,13 +98,16 @@ namespace Umbraco.Core.Persistence.SqlSyntax return result > 0; } - protected override string FormatIdentity(DatabaseModelDefinitions.ColumnDefinition column) + protected override string FormatIdentity(ColumnDefinition column) { return column.IsIdentity ? GetIdentityString(column) : string.Empty; } - private static string GetIdentityString(DatabaseModelDefinitions.ColumnDefinition column) + private static string GetIdentityString(ColumnDefinition column) { + if (column.Seeding != default(int)) + return string.Format("IDENTITY({0},1)", column.Seeding); + return "IDENTITY(1,1)"; } @@ -123,5 +129,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax } public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } } + + public override string DropIndex { get { return "DROP INDEX {1}.{0}"; } } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 527ad161b6..995320a037 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -34,6 +34,21 @@ namespace Umbraco.Core.Persistence.SqlSyntax InitColumnTypeMap(); } + public override string GetQuotedTableName(string tableName) + { + return string.Format("[{0}]", tableName); + } + + public override string GetQuotedColumnName(string columnName) + { + return string.Format("[{0}]", columnName); + } + + public override string GetQuotedName(string name) + { + return string.Format("[{0}]", name); + } + public override bool DoesTableExist(Database db, string tableName) { var result = @@ -43,6 +58,16 @@ namespace Umbraco.Core.Persistence.SqlSyntax return result > 0; } + public override string FormatColumnRename(string tableName, string oldName, string newName) + { + return string.Format(RenameColumn, tableName, oldName, newName); + } + + public override string FormatTableRename(string oldName, string newName) + { + return string.Format(RenameTable, oldName, newName); + } + protected override string FormatIdentity(ColumnDefinition column) { return column.IsIdentity ? GetIdentityString(column) : string.Empty; @@ -70,6 +95,34 @@ namespace Umbraco.Core.Persistence.SqlSyntax return null; } + public override string DeleteDefaultConstraint + { + get + { + return "DECLARE @default sysname, @sql nvarchar(max);\r\n\r\n" + + "-- get name of default constraint\r\n" + + "SELECT @default = name\r\n" + + "FROM sys.default_constraints\r\n" + + "WHERE parent_object_id = object_id('{0}')\r\n" + "" + + "AND type = 'D'\r\n" + "" + + "AND parent_column_id = (\r\n" + "" + + "SELECT column_id\r\n" + + "FROM sys.columns\r\n" + + "WHERE object_id = object_id('{0}')\r\n" + + "AND name = '{1}'\r\n" + + ");\r\n\r\n" + + "-- create alter table command to drop contraint as string and run it\r\n" + + "SET @sql = N'ALTER TABLE {0} DROP CONSTRAINT ' + @default;\r\n" + + "EXEC sp_executesql @sql;"; + } + } + public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } } + + public override string DropIndex { get { return "DROP INDEX {0} ON {1}"; } } + + public override string RenameColumn { get { return "sp_rename '{0}.{1}', '{2}', 'COLUMN'"; } } + + public override string RenameTable { get { return "sp_rename '{0}', '{1}'"; } } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 5e0bd84f4f..15b84189a0 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -5,8 +5,6 @@ using System.Linq; using System.Text; using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using ColumnDefinition = Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions.ColumnDefinition; -using TableDefinition = Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions.TableDefinition; namespace Umbraco.Core.Persistence.SqlSyntax { @@ -22,11 +20,12 @@ namespace Umbraco.Core.Persistence.SqlSyntax { protected SqlSyntaxProviderBase() { - ClauseOrder = new List> + ClauseOrder = new List> { FormatString, FormatType, FormatNullable, + FormatConstraint, FormatDefaultValue, FormatPrimaryKey, FormatIdentity @@ -36,7 +35,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})"; public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})"; - public string DefaultValueFormat = " DEFAULT ({0})"; + public string DefaultValueFormat = "DEFAULT ({0})"; public int DefaultStringLength = 255; //Set by Constructor @@ -54,7 +53,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax public string DateTimeColumnDefinition = "DATETIME"; public string TimeColumnDefinition = "DATETIME"; - protected IList> ClauseOrder { get; set; } + protected IList> ClauseOrder { get; set; } protected DbTypes DbTypeMap = new DbTypes(); protected void InitColumnTypeMap() @@ -152,190 +151,69 @@ namespace Umbraco.Core.Persistence.SqlSyntax return "NVARCHAR"; } - public virtual string GetColumnDefinition(ColumnDefinition column, string tableName) - { - string dbTypeDefinition; - if (column.HasSpecialDbType) - { - if (column.DbTypeLength.HasValue) - { - dbTypeDefinition = string.Format("{0}({1})", - GetSpecialDbType(column.DbType), - column.DbTypeLength.Value); - } - else - { - dbTypeDefinition = GetSpecialDbType(column.DbType); - } - } - else if (column.PropertyType == typeof(string)) - { - dbTypeDefinition = string.Format(StringLengthColumnDefinitionFormat, column.DbTypeLength.GetValueOrDefault(DefaultStringLength)); - } - else - { - if (!DbTypeMap.ColumnTypeMap.TryGetValue(column.PropertyType, out dbTypeDefinition)) - { - dbTypeDefinition = ""; - } - } - - var sql = new StringBuilder(); - sql.AppendFormat("{0} {1}", GetQuotedColumnName(column.ColumnName), dbTypeDefinition); - - if (column.IsPrimaryKeyIdentityColumn) - { - sql.Append(" NOT NULL ").Append(AutoIncrementDefinition); - } - else - { - sql.Append(column.IsNullable ? " NULL" : " NOT NULL"); - } - - if(column.HasConstraint) - { - sql.Append(GetConstraintDefinition(column, tableName)); - } - - return sql.ToString(); - } - - public virtual string GetConstraintDefinition(ColumnDefinition column, string tableName) - { - var sql = new StringBuilder(); - sql.AppendFormat(" CONSTRAINT {0}", - string.IsNullOrEmpty(column.ConstraintName) - ? GetQuotedName(string.Format("DF_{0}_{1}", tableName, column.ColumnName)) - : column.ConstraintName); - - string value = column.PropertyType == typeof (string) - ? GetQuotedValue(column.ConstraintDefaultValue) - : column.ConstraintDefaultValue; - - sql.AppendFormat(DefaultValueFormat, value); - return sql.ToString(); - } - - public virtual string GetPrimaryKeyStatement(ColumnDefinition column, string tableName) - { - string constraintName = string.IsNullOrEmpty(column.PrimaryKeyName) - ? string.Format("PK_{0}", tableName) - : column.PrimaryKeyName; - - string columns = string.IsNullOrEmpty(column.PrimaryKeyColumns) - ? GetQuotedColumnName(column.ColumnName) - : column.PrimaryKeyColumns; - - string sql = string.Format("ALTER TABLE {0} ADD CONSTRAINT {1} PRIMARY KEY {2} ({3}); \n", - GetQuotedTableName(tableName), - GetQuotedName(constraintName), - column.IsPrimaryKeyClustered ? "CLUSTERED" : "NONCLUSTERED", - columns); - return sql; - } - - public virtual string ToCreateTableStatement(TableDefinition table) - { - var columns = new StringBuilder(); - - foreach (var column in table.ColumnDefinitions) - { - columns.Append(GetColumnDefinition(column, table.TableName) + ", \n"); - } - - string sql = string.Format("CREATE TABLE {0} \n(\n {1} \n); \n", - table.TableName, - columns.ToString().TrimEnd(", \n".ToCharArray())); - - return sql; - } - - public virtual string ToCreatePrimaryKeyStatement(TableDefinition table) - { - var columnDefinition = table.ColumnDefinitions.FirstOrDefault(x => x.IsPrimaryKey); - if (columnDefinition == null) - return string.Empty; - - var sql = GetPrimaryKeyStatement(columnDefinition, table.TableName); - return sql; - } - - public virtual List ToCreateForeignKeyStatements(TableDefinition table) - { - var foreignKeys = new List(); - - foreach (var key in table.ForeignKeyDefinitions) - { - string constraintName = string.IsNullOrEmpty(key.ConstraintName) - ? string.Format("FK_{0}_{1}_{2}", table.TableName, key.ReferencedTableName, key.ReferencedColumnName) - : key.ConstraintName; - - foreignKeys.Add(string.Format("ALTER TABLE {0} ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3} ({4}); \n", - GetQuotedTableName(table.TableName), - GetQuotedName(constraintName), - GetQuotedColumnName(key.ColumnName), - GetQuotedTableName(key.ReferencedTableName), - GetQuotedColumnName(key.ReferencedColumnName))); - } - - return foreignKeys; - } - - public virtual List ToCreateIndexStatements(TableDefinition table) - { - var indexes = new List(); - - foreach (var index in table.IndexDefinitions) - { - string name = string.IsNullOrEmpty(index.IndexName) - ? string.Format("IX_{0}_{1}", table.TableName, index.IndexForColumn) - : index.IndexName; - - string columns = string.IsNullOrEmpty(index.ColumnNames) - ? GetQuotedColumnName(index.IndexForColumn) - : index.ColumnNames; - - indexes.Add(string.Format("CREATE {0} INDEX {1} ON {2} ({3}); \n", - GetIndexType(index.IndexType), - GetQuotedName(name), - GetQuotedTableName(table.TableName), - columns)); - } - return indexes; - } - - public virtual List ToAlterIdentitySeedStatements(TableDefinition table) - { - var seeds = new List(); - - foreach (var definition in table.ColumnDefinitions) - { - if (definition.PrimaryKeySeeding > 0) - { - seeds.Add(string.Format("ALTER TABLE {0} ALTER COLUMN {1} IDENTITY({2},1); \n", - GetQuotedTableName(table.TableName), - GetQuotedColumnName(definition.ColumnName), - definition.PrimaryKeySeeding)); - } - } - - return seeds; - } - public virtual bool DoesTableExist(Database db, string tableName) { return false; } - public virtual DbType GetColumnDbType(Type valueType) + public virtual string Format(TableDefinition table) { - if (valueType.IsEnum) - return DbTypeMap.ColumnDbTypeMap[typeof(string)]; + var statement = string.Format(CreateTable, GetQuotedTableName(table.Name), Format(table.Columns)); - return DbTypeMap.ColumnDbTypeMap[valueType]; + return statement; } - public virtual string Format(DatabaseModelDefinitions.ColumnDefinition column) + public virtual List Format(IEnumerable indexes) + { + return indexes.Select(Format).ToList(); + } + + public virtual string Format(IndexDefinition index) + { + string name = string.IsNullOrEmpty(index.Name) + ? string.Format("IX_{0}_{1}", index.TableName, index.ColumnName) + : index.Name; + + string columns = index.Columns.Any() + ? string.Join(",", index.Columns.Select(x => GetQuotedColumnName(x.Name))) + : GetQuotedColumnName(index.ColumnName); + + return string.Format(CreateIndex, GetIndexType(index.IndexType), " ", GetQuotedName(name), + GetQuotedTableName(index.TableName), columns); + } + + public virtual List Format(IEnumerable foreignKeys) + { + return foreignKeys.Select(Format).ToList(); + } + + public virtual string Format(ForeignKeyDefinition foreignKey) + { + string constraintName = string.IsNullOrEmpty(foreignKey.Name) + ? string.Format("FK_{0}_{1}_{2}", foreignKey.ForeignTable, foreignKey.PrimaryTable, foreignKey.PrimaryColumns.First()) + : foreignKey.Name; + + return string.Format(CreateForeignKeyConstraint, + GetQuotedTableName(foreignKey.ForeignTable), + GetQuotedName(constraintName), + GetQuotedColumnName(foreignKey.ForeignColumns.First()), + GetQuotedTableName(foreignKey.PrimaryTable), + GetQuotedColumnName(foreignKey.PrimaryColumns.First()), + FormatCascade("DELETE", foreignKey.OnDelete), + FormatCascade("UPDATE", foreignKey.OnUpdate)); + } + + public virtual string Format(IEnumerable columns) + { + var sb = new StringBuilder(); + foreach (var column in columns) + { + sb.Append(Format(column) +",\n"); + } + return sb.ToString().TrimEnd(",\n"); + } + + public virtual string Format(ColumnDefinition column) { var clauses = new List(); @@ -349,19 +227,96 @@ namespace Umbraco.Core.Persistence.SqlSyntax return string.Join(" ", clauses.ToArray()); } - public virtual string FormatString(DatabaseModelDefinitions.ColumnDefinition column) + public virtual string FormatPrimaryKey(TableDefinition table) + { + var columnDefinition = table.Columns.FirstOrDefault(x => x.IsPrimaryKey); + if (columnDefinition == null) + return string.Empty; + + string constraintName = string.IsNullOrEmpty(columnDefinition.PrimaryKeyName) + ? string.Format("PK_{0}", table.Name) + : columnDefinition.PrimaryKeyName; + + string columns = string.IsNullOrEmpty(columnDefinition.PrimaryKeyColumns) + ? GetQuotedColumnName(columnDefinition.Name) + : columnDefinition.PrimaryKeyColumns; + + string primaryKeyPart = string.Concat("PRIMARY KEY", columnDefinition.IsIndexed ? " CLUSTERED" : " NONCLUSTERED"); + + return string.Format(CreateConstraint, + GetQuotedTableName(table.Name), + GetQuotedName(constraintName), + primaryKeyPart, + columns); + } + + public virtual string FormatColumnRename(string tableName, string oldName, string newName) + { + return string.Format(RenameColumn, + GetQuotedTableName(tableName), + GetQuotedColumnName(oldName), + GetQuotedColumnName(newName)); + } + + public virtual string FormatTableRename(string oldName, string newName) + { + return string.Format(RenameTable, GetQuotedTableName(oldName), GetQuotedTableName(newName)); + } + + protected virtual string FormatCascade(string onWhat, Rule rule) + { + string action = "NO ACTION"; + switch (rule) + { + case Rule.None: + return ""; + case Rule.Cascade: + action = "CASCADE"; + break; + case Rule.SetNull: + action = "SET NULL"; + break; + case Rule.SetDefault: + action = "SET DEFAULT"; + break; + } + + return string.Format(" ON {0} {1}", onWhat, action); + } + + protected virtual string FormatString(ColumnDefinition column) { return GetQuotedColumnName(column.Name); } - protected virtual string FormatType(DatabaseModelDefinitions.ColumnDefinition column) + protected virtual string FormatType(ColumnDefinition column) { - if (!column.Type.HasValue) + if (column.Type.HasValue == false && string.IsNullOrEmpty(column.CustomType) == false) return column.CustomType; - var dbType = DbTypeMap.ColumnDbTypeMap.First(x => x.Value == column.Type.Value).Key; - var definition = DbTypeMap.ColumnTypeMap.First(x => x.Key == dbType).Value; + if (column.HasSpecialDbType) + { + if (column.Size != default(int)) + { + return string.Format("{0}({1})", + GetSpecialDbType(column.DbType), + column.Size); + } + return GetSpecialDbType(column.DbType); + } + + Type type = column.Type.HasValue + ? DbTypeMap.ColumnDbTypeMap.First(x => x.Value == column.Type.Value).Key + : column.PropertyType; + + if (type == typeof (string)) + { + var valueOrDefault = column.Size != default(int) ? column.Size : DefaultStringLength; + return string.Format(StringLengthColumnDefinitionFormat, valueOrDefault); + } + + string definition = DbTypeMap.ColumnTypeMap.First(x => x.Key == type).Value; string dbTypeDefinition = column.Size != default(int) ? string.Format("{0}({1})", definition, column.Size) : definition; @@ -369,12 +324,23 @@ namespace Umbraco.Core.Persistence.SqlSyntax return dbTypeDefinition; } - protected virtual string FormatNullable(DatabaseModelDefinitions.ColumnDefinition column) + protected virtual string FormatNullable(ColumnDefinition column) { - return column.IsNullable ? string.Empty : "NOT NULL"; + return column.IsNullable ? "NULL" : "NOT NULL"; } - protected virtual string FormatDefaultValue(DatabaseModelDefinitions.ColumnDefinition column) + protected virtual string FormatConstraint(ColumnDefinition column) + { + if (string.IsNullOrEmpty(column.ConstraintName) && column.DefaultValue == null) + return string.Empty; + + return string.Format("CONSTRAINT {0}", + string.IsNullOrEmpty(column.ConstraintName) + ? GetQuotedName(string.Format("DF_{0}_{1}", column.TableName, column.Name)) + : column.ConstraintName); + } + + protected virtual string FormatDefaultValue(ColumnDefinition column) { if (column.DefaultValue == null) return string.Empty; @@ -386,20 +352,28 @@ namespace Umbraco.Core.Persistence.SqlSyntax if (string.IsNullOrEmpty(method)) return string.Empty; - return "DEFAULT " + method; + return string.Format(DefaultValueFormat, method); } - return "DEFAULT " + GetQuotedValue(column.DefaultValue.ToString()); + return string.Format(DefaultValueFormat, GetQuotedValue(column.DefaultValue.ToString())); } - protected virtual string FormatPrimaryKey(DatabaseModelDefinitions.ColumnDefinition column) + protected virtual string FormatPrimaryKey(ColumnDefinition column) { return string.Empty; } protected abstract string FormatSystemMethods(SystemMethods systemMethod); - protected abstract string FormatIdentity(DatabaseModelDefinitions.ColumnDefinition column); + protected abstract string FormatIdentity(ColumnDefinition column); + + public virtual string DeleteDefaultConstraint + { + get + { + throw new NotSupportedException("Default constraints are not supported"); + } + } public virtual string CreateTable { get { return "CREATE TABLE {0} ({1})"; } } public virtual string DropTable { get { return "DROP TABLE {0}"; } } diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 6f9f5fc220..4cdf4de85e 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -93,7 +93,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// public void Commit() { - using (var transaction = Database.GetTransaction()) { foreach (var operation in _operations.OrderBy(o => o.ProcessDate)) @@ -113,7 +112,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork } transaction.Complete(); } - // Clear everything _operations.Clear(); diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 21ab8cded4..fbdae5b5e4 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -1,5 +1,4 @@ -using System; -using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration; namespace Umbraco.Core.Persistence.UnitOfWork { @@ -19,6 +18,14 @@ namespace Umbraco.Core.Persistence.UnitOfWork } + /// + /// Constructor accepting custom connectino string and provider name + /// + /// Connection String to use with Database + /// Database Provider for the Connection String + public PetaPocoUnitOfWorkProvider(string connectionString, string providerName) : this(new DefaultDatabaseFactory(connectionString, providerName)) + {} + /// /// Constructor accepting an IDatabaseFactory instance /// diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 7ed0ea82bb..80ec72c722 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -70,34 +70,44 @@ namespace Umbraco.Core.Services /// public IContent CreateContent(int parentId, string contentTypeAlias, int userId = -1) { + IContentType contentType = null; + IContent content = null; + var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentTypeRepository(uow)) - { - var query = Query.Builder.Where(x => x.Alias == contentTypeAlias); - var contentTypes = repository.GetByQuery(query); + using (var repository = _repositoryFactory.CreateContentTypeRepository(uow)) + { + var query = Query.Builder.Where(x => x.Alias == contentTypeAlias); + var contentTypes = repository.GetByQuery(query); - if (!contentTypes.Any()) - throw new Exception(string.Format("No ContentType matching the passed in Alias: '{0}' was found", contentTypeAlias)); + if (!contentTypes.Any()) + throw new Exception(string.Format("No ContentType matching the passed in Alias: '{0}' was found", + contentTypeAlias)); - var contentType = contentTypes.First(); + contentType = contentTypes.First(); - if (contentType == null) - throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null", contentTypeAlias)); + if (contentType == null) + throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null", + contentTypeAlias)); + } - var content = new Content(parentId, contentType); - - if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this)) - return content; + content = new Content(parentId, contentType); + var e = new NewEventArgs { Alias = contentTypeAlias, ParentId = parentId }; + if (Creating != null) + Creating(content, e); - SetUser(content, userId); - SetWriter(content, userId); + if (!e.Cancel) + { + SetUser(content, userId); + SetWriter(content, userId); - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); + if (Created != null) + Created(content, e); - Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); + Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id); + } - return content; - } + return content; + } /// @@ -128,7 +138,6 @@ namespace Umbraco.Core.Services } } - /// /// Gets a collection of objects by the Id of the /// @@ -161,6 +170,33 @@ namespace Umbraco.Core.Services } } + /// + /// Gets a specific version of an item. + /// + /// Id of the version to retrieve + /// An item + public IContent GetByVersion(Guid versionId) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetByVersion(versionId); + } + } + + /// + /// Gets a collection of an objects versions by Id + /// + /// + /// An Enumerable list of objects + public IEnumerable GetVersions(int id) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var versions = repository.GetAllVersions(id); + return versions; + } + } + /// /// Gets a collection of objects by Parent Id /// @@ -221,35 +257,6 @@ namespace Umbraco.Core.Services } } - - /// - /// Gets a specific version of an item. - /// - /// Id of the to retrieve version from - /// Id of the version to retrieve - /// An item - public IContent GetByIdVersion(int id, Guid versionId) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetByVersion(id, versionId); - } - } - - /// - /// Gets a collection of an objects versions by Id - /// - /// - /// An Enumerable list of objects - public IEnumerable GetVersions(int id) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var versions = repository.GetAllVersions(id); - return versions; - } - } - /// /// Gets the published version of an item /// @@ -257,11 +264,8 @@ namespace Umbraco.Core.Services /// An item public IContent GetPublishedVersion(int id) { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - var version = GetVersions(id); - return version.FirstOrDefault(x => x.Published == true); - } + var version = GetVersions(id); + return version.FirstOrDefault(x => x.Published == true); } /// @@ -354,69 +358,70 @@ namespace Umbraco.Core.Services } } - /// - /// Re-Publishes all Content - /// - /// Optional Id of the User issueing the publishing - /// Optional boolean to avoid having the cache refreshed when calling this RePublish method. By default this method will update the cache. - /// True if publishing succeeded, otherwise False - public bool RePublishAll(int userId = -1, bool omitCacheRefresh = false) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - var list = new List(); - var updated = new List(); + /// + /// Re-Publishes all Content + /// + /// Optional Id of the User issueing the publishing + /// Optional boolean to avoid having the cache refreshed when calling this RePublish method. By default this method will update the cache. + /// True if publishing succeeded, otherwise False + public bool RePublishAll(int userId = -1, bool omitCacheRefresh = false) + { + //TODO Refactor this so omitCacheRefresh isn't exposed in the public method, but only in an internal one as its purely there for legacy reasons. + var list = new List(); + var updated = new List(); - //Consider creating a Path query instead of recursive method: - //var query = Query.Builder.Where(x => x.Path.StartsWith("-1")); + //Consider creating a Path query instead of recursive method: + //var query = Query.Builder.Where(x => x.Path.StartsWith("-1")); - var rootContent = GetRootContent(); - foreach (var content in rootContent) - { - if (content.IsValid()) - { - list.Add(content); - list.AddRange(GetChildrenDeep(content.Id)); - } - } + var rootContent = GetRootContent(); + foreach (var content in rootContent) + { + if (content.IsValid()) + { + list.Add(content); + list.AddRange(GetChildrenDeep(content.Id)); + } + } - //Publish and then update the database with new status - var published = _publishingStrategy.PublishWithChildren(list, userId); - if (published) - { - //Only loop through content where the Published property has been updated - foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published"))) - { - SetWriter(item, userId); - repository.AddOrUpdate(item); - updated.Add(item); - } + //Publish and then update the database with new status + var published = _publishingStrategy.PublishWithChildren(list, userId); + if (published) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + //Only loop through content where the Published property has been updated + foreach (var item in list.Where(x => ((ICanBeDirty) x).IsPropertyDirty("Published"))) + { + SetWriter(item, userId); + repository.AddOrUpdate(item); + updated.Add(item); + } - uow.Commit(); + uow.Commit(); - foreach (var c in updated) - { - var xml = c.ToXml(); - var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) }; - var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new { Id = c.Id }) != null; - int result = exists - ? uow.Database.Update(poco) - : Convert.ToInt32(uow.Database.Insert(poco)); - } + foreach (var c in updated) + { + var xml = c.ToXml(); + var poco = new ContentXmlDto {NodeId = c.Id, Xml = xml.ToString(SaveOptions.None)}; + var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new {Id = c.Id}) != + null; + int result = exists + ? uow.Database.Update(poco) + : Convert.ToInt32(uow.Database.Insert(poco)); + } + } + //Updating content to published state is finished, so we fire event through PublishingStrategy to have cache updated + if (omitCacheRefresh == false) + _publishingStrategy.PublishingFinalized(updated, true); + } - //Updating content to published state is finished, so we fire event through PublishingStrategy to have cache updated - if (omitCacheRefresh == false) - _publishingStrategy.PublishingFinalized(updated, true); + Audit.Add(AuditTypes.Publish, "RePublish All performed by user", userId == -1 ? 0 : userId, -1); - Audit.Add(AuditTypes.Publish, "RePublish All performed by user", userId == -1 ? 0 : userId, -1); - } + return published; + } - return published; - } - } - - /// + /// /// Publishes a single object /// /// The to publish @@ -425,310 +430,346 @@ namespace Umbraco.Core.Services /// True if publishing succeeded, otherwise False public bool Publish(IContent content, int userId = -1, bool omitCacheRefresh = false) { + //TODO Refactor this so omitCacheRefresh isn't exposed in the public method, but only in an internal one as its purely there for legacy reasons. return SaveAndPublish(content, userId, omitCacheRefresh); } - /// - /// Publishes a object and all its children - /// - /// The to publish along with its children - /// Optional Id of the User issueing the publishing - /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will update the cache. - /// True if publishing succeeded, otherwise False - public bool PublishWithChildren(IContent content, int userId = -1, bool omitCacheRefresh = false) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published - if (content.ParentId != -1 && content.ParentId != -20 && HasPublishedVersion(content.ParentId) == false) - { - LogHelper.Info( - string.Format("Content '{0}' with Id '{1}' could not be published because its parent is not published.", - content.Name, content.Id)); - return false; - } + /// + /// Publishes a object and all its children + /// + /// The to publish along with its children + /// Optional Id of the User issueing the publishing + /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will update the cache. + /// True if publishing succeeded, otherwise False + public bool PublishWithChildren(IContent content, int userId = -1, bool omitCacheRefresh = false) + { + //TODO Refactor this so omitCacheRefresh isn't exposed in the public method, but only in an internal one as its purely there for legacy reasons. - //Content contains invalid property values and can therefore not be published - fire event? - if (!content.IsValid()) - { - LogHelper.Info( - string.Format("Content '{0}' with Id '{1}' could not be published because of invalid properties.", - content.Name, content.Id)); - return false; - } + //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published + if (content.ParentId != -1 && content.ParentId != -20 && HasPublishedVersion(content.ParentId) == false) + { + LogHelper.Info( + string.Format( + "Content '{0}' with Id '{1}' could not be published because its parent is not published.", + content.Name, content.Id)); + return false; + } - //Consider creating a Path query instead of recursive method: - //var query = Query.Builder.Where(x => x.Path.StartsWith(content.Path)); + //Content contains invalid property values and can therefore not be published - fire event? + if (!content.IsValid()) + { + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' could not be published because of invalid properties.", + content.Name, content.Id)); + return false; + } - var updated = new List(); - var list = new List(); - list.Add(content); - list.AddRange(GetChildrenDeep(content.Id)); + //Consider creating a Path query instead of recursive method: + //var query = Query.Builder.Where(x => x.Path.StartsWith(content.Path)); - //Publish and then update the database with new status - var published = _publishingStrategy.PublishWithChildren(list, userId); - if (published) - { - //Only loop through content where the Published property has been updated - foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published"))) - { - SetWriter(item, userId); - repository.AddOrUpdate(item); - updated.Add(item); - } + var updated = new List(); + var list = new List(); + list.Add(content); + list.AddRange(GetChildrenDeep(content.Id)); - uow.Commit(); + //Publish and then update the database with new status + var published = _publishingStrategy.PublishWithChildren(list, userId); + if (published) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + //Only loop through content where the Published property has been updated + foreach (var item in list.Where(x => ((ICanBeDirty) x).IsPropertyDirty("Published"))) + { + SetWriter(item, userId); + repository.AddOrUpdate(item); + updated.Add(item); + } - foreach (var c in updated) - { - var xml = c.ToXml(); - var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) }; - var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new { Id = c.Id }) != null; - int result = exists - ? uow.Database.Update(poco) - : Convert.ToInt32(uow.Database.Insert(poco)); - } + uow.Commit(); - //Save xml to db and call following method to fire event: - if (omitCacheRefresh == false) - _publishingStrategy.PublishingFinalized(updated, false); + foreach (var c in updated) + { + var xml = c.ToXml(); + var poco = new ContentXmlDto {NodeId = c.Id, Xml = xml.ToString(SaveOptions.None)}; + var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new {Id = c.Id}) != + null; + int result = exists + ? uow.Database.Update(poco) + : Convert.ToInt32(uow.Database.Insert(poco)); + } + } + //Save xml to db and call following method to fire event: + if (omitCacheRefresh == false) + _publishingStrategy.PublishingFinalized(updated, false); - Audit.Add(AuditTypes.Publish, "Publish with Children performed by user", userId == -1 ? 0 : userId, content.Id); - } + Audit.Add(AuditTypes.Publish, "Publish with Children performed by user", userId == -1 ? 0 : userId, + content.Id); + } - return published; - } - } + return published; + } - /// - /// UnPublishes a single object - /// - /// The to publish - /// Optional Id of the User issueing the publishing - /// Optional boolean to avoid having the cache refreshed when calling this Unpublish method. By default this method will update the cache. - /// True if unpublishing succeeded, otherwise False - public bool UnPublish(IContent content, int userId = -1, bool omitCacheRefresh = false) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - //Look for children and unpublish them if any exists, otherwise just unpublish the passed in Content. - var children = GetChildrenDeep(content.Id); - var hasChildren = children.Any(); + /// + /// UnPublishes a single object + /// + /// The to publish + /// Optional Id of the User issueing the publishing + /// Optional boolean to avoid having the cache refreshed when calling this Unpublish method. By default this method will update the cache. + /// True if unpublishing succeeded, otherwise False + public bool UnPublish(IContent content, int userId = -1, bool omitCacheRefresh = false) + { + //TODO Refactor this so omitCacheRefresh isn't exposed in the public method, but only in an internal one as its purely there for legacy reasons. - if (hasChildren) - children.Add(content); + //Look for children and unpublish them if any exists, otherwise just unpublish the passed in Content. + var children = GetChildrenDeep(content.Id); + var hasChildren = children.Any(); - var unpublished = hasChildren - ? _publishingStrategy.UnPublish(children, userId) - : _publishingStrategy.UnPublish(content, userId); + if (hasChildren) + children.Add(content); - if (unpublished) - { - repository.AddOrUpdate(content); + var unpublished = hasChildren + ? _publishingStrategy.UnPublish(children, userId) + : _publishingStrategy.UnPublish(content, userId); - if (hasChildren) - { - foreach (var child in children) - { - SetWriter(child, userId); - repository.AddOrUpdate(child); - } - } + if (unpublished) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + repository.AddOrUpdate(content); - uow.Commit(); + if (hasChildren) + { + foreach (var child in children) + { + SetWriter(child, userId); + repository.AddOrUpdate(child); + } + } - //Remove 'published' xml from the cmsContentXml table for the unpublished content and its (possible) children - uow.Database.Delete("WHERE nodeId = @Id", new { Id = content.Id }); - if (hasChildren) - { - foreach (var child in children) - { - uow.Database.Delete("WHERE nodeId = @Id", new { Id = child.Id }); - } - } + //Remove 'published' xml from the cmsContentXml table for the unpublished content and its (possible) children + uow.Database.Delete("WHERE nodeId = @Id", new {Id = content.Id}); + if (hasChildren) + { + foreach (var child in children) + { + uow.Database.Delete("WHERE nodeId = @Id", new {Id = child.Id}); + } + } - //Delete xml from db? and call following method to fire event through PublishingStrategy to update cache - if (omitCacheRefresh == false) - _publishingStrategy.UnPublishingFinalized(content); + uow.Commit(); + } + //Delete xml from db? and call following method to fire event through PublishingStrategy to update cache + if (omitCacheRefresh == false) + _publishingStrategy.UnPublishingFinalized(content); - Audit.Add(AuditTypes.UnPublish, "UnPublish performed by user", userId == -1 ? 0 : userId, content.Id); - } + Audit.Add(AuditTypes.UnPublish, "UnPublish performed by user", userId == -1 ? 0 : userId, content.Id); + } - return unpublished; - } - } + return unpublished; + } - /// - /// Gets a flat list of decendents of content from parent id - /// - /// - /// Only contains valid objects, which means - /// that everything in the returned list can be published. - /// If an invalid object is found it will not - /// be added to the list neither will its children. - /// - /// Id of the parent to retrieve children from - /// A list of valid that can be published - private List GetChildrenDeep(int parentId) - { - var list = new List(); - var children = GetChildren(parentId); - foreach (var child in children) - { - if (child.IsValid()) - { - list.Add(child); - list.AddRange(GetChildrenDeep(child.Id)); - } - } - return list; - } + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional Id of the User issueing the publishing + /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will update the cache. + /// True if publishing succeeded, otherwise False + public bool SaveAndPublish(IContent content, int userId = -1, bool omitCacheRefresh = false) + { + //TODO Refactor this so omitCacheRefresh isn't exposed in the public method, but only in an internal one as its purely there for legacy reasons. + var e = new SaveEventArgs(); + if (Saving != null) + Saving(content, e); - /// - /// Saves and Publishes a single object - /// - /// The to save and publish - /// Optional Id of the User issueing the publishing - /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will update the cache. - /// True if publishing succeeded, otherwise False - public bool SaveAndPublish(IContent content, int userId = -1, bool omitCacheRefresh = false) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) - return false; + if (!e.Cancel) + { + //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published + if (content.ParentId != -1 && content.ParentId != -20 && HasPublishedVersion(content.ParentId) == false) + { + LogHelper.Info( + string.Format( + "Content '{0}' with Id '{1}' could not be published because its parent is not published.", + content.Name, content.Id)); + return false; + } - //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published - if (content.ParentId != -1 && content.ParentId != -20 && HasPublishedVersion(content.ParentId) == false) - { - LogHelper.Info( - string.Format( - "Content '{0}' with Id '{1}' could not be published because its parent is not published.", - content.Name, content.Id)); - return false; - } + //Content contains invalid property values and can therefore not be published - fire event? + if (!content.IsValid()) + { + LogHelper.Info( + string.Format( + "Content '{0}' with Id '{1}' could not be published because of invalid properties.", + content.Name, content.Id)); + return false; + } - //Content contains invalid property values and can therefore not be published - fire event? - if (!content.IsValid()) - { - LogHelper.Info( - string.Format( - "Content '{0}' with Id '{1}' could not be published because of invalid properties.", - content.Name, content.Id)); - return false; - } + //Publish and then update the database with new status + bool published = _publishingStrategy.Publish(content, userId); - //Publish and then update the database with new status - bool published = _publishingStrategy.Publish(content, userId); + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + //Since this is the Save and Publish method, the content should be saved even though the publish fails or isn't allowed + SetWriter(content, userId); + repository.AddOrUpdate(content); - //Since this is the Save and Publish method, the content should be saved even though the publish fails or isn't allowed - SetWriter(content, userId); - repository.AddOrUpdate(content); uow.Commit(); - if (published) - { - var xml = content.ToXml(); - var poco = new ContentXmlDto { NodeId = content.Id, Xml = xml.ToString(SaveOptions.None) }; - var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new { Id = content.Id }) != null; - int result = exists - ? uow.Database.Update(poco) - : Convert.ToInt32(uow.Database.Insert(poco)); + if (published) + { + var xml = content.ToXml(); + var poco = new ContentXmlDto {NodeId = content.Id, Xml = xml.ToString(SaveOptions.None)}; + var exists = + uow.Database.FirstOrDefault("WHERE nodeId = @Id", new {Id = content.Id}) != + null; + int result = exists + ? uow.Database.Update(poco) + : Convert.ToInt32(uow.Database.Insert(poco)); + } + } - //Save xml to db and call following method to fire event through PublishingStrategy to update cache - if (omitCacheRefresh == false) - _publishingStrategy.PublishingFinalized(content); - } + //Save xml to db and call following method to fire event through PublishingStrategy to update cache + if (omitCacheRefresh == false) + _publishingStrategy.PublishingFinalized(content); - Saved.RaiseEvent(new SaveEventArgs(content, false), this); + if (Saved != null) + Saved(content, e); - Audit.Add(AuditTypes.Publish, "Save and Publish performed by user", userId == -1 ? 0 : userId, content.Id); + Audit.Add(AuditTypes.Publish, "Save and Publish performed by user", userId == -1 ? 0 : userId, content.Id); - return published; - } - } + return published; + } + return false; + } - /// - /// Saves a single object - /// - /// The to save - /// Optional Id of the User saving the Content - public void Save(IContent content, int userId = -1) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) - return; + /// + /// Saves a single object + /// + /// The to save + /// Optional Id of the User saving the Content + public void Save(IContent content, int userId = -1) + { + var e = new SaveEventArgs(); + if (Saving != null) + Saving(content, e); - SetWriter(content, userId); + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + SetWriter(content, userId); - //Only change the publish state if the "previous" version was actually published - if (content.Published) - content.ChangePublishedState(false); + //Only change the publish state if the "previous" version was actually published + if (content.Published) + content.ChangePublishedState(false); - repository.AddOrUpdate(content); - uow.Commit(); - - Saved.RaiseEvent(new SaveEventArgs(content, false), this); + repository.AddOrUpdate(content); + uow.Commit(); + } + if (Saved != null) + Saved(content, e); - Audit.Add(AuditTypes.Save, "Save Content performed by user", userId == -1 ? 0 : userId, content.Id); - } - } + Audit.Add(AuditTypes.Save, "Save Content performed by user", userId == -1 ? 0 : userId, content.Id); + } + } - /// - /// Saves a collection of objects. - /// - /// - /// If the collection of content contains new objects that references eachother by Id or ParentId, - /// then use the overload Save method with a collection of Lazy . - /// - /// Collection of to save - /// Optional Id of the User saving the Content - public void Save(IEnumerable contents, int userId = -1) - { + /// + /// Saves a collection of objects. + /// + /// + /// If the collection of content contains new objects that references eachother by Id or ParentId, + /// then use the overload Save method with a collection of Lazy . + /// + /// Collection of to save + /// Optional Id of the User saving the Content + public void Save(IEnumerable contents, int userId = -1) + { if (SavingCollection.IsRaisedEventCancelled(new SaveEventArgs>(contents), this)) return; - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - var containsNew = contents.Any(x => x.HasIdentity == false); + var containsNew = contents.Any(x => x.HasIdentity == false); - if (containsNew) - { - foreach (var content in contents) - { + var e = new SaveEventArgs(); + if (Saving != null) + Saving(contents, e); + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + if (containsNew) + { + foreach (var content in contents) + { - SetWriter(content, userId); + SetWriter(content, userId); - //Only change the publish state if the "previous" version was actually published - if (content.Published) - content.ChangePublishedState(false); + //Only change the publish state if the "previous" version was actually published + if (content.Published) + content.ChangePublishedState(false); - repository.AddOrUpdate(content); - } - } - else - { - foreach (var content in contents) - { - SetWriter(content, userId); - repository.AddOrUpdate(content); - } - } + repository.AddOrUpdate(content); + uow.Commit(); + } + } + else + { + foreach (var content in contents) + { + SetWriter(content, userId); + repository.AddOrUpdate(content); + } + uow.Commit(); + } + } + if (Saved != null) + Saved(contents, e); + Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, -1); + } + } - //Commit everything in one go - uow.Commit(); + /// + /// Saves a collection of lazy loaded objects. + /// + /// + /// This method ensures that Content is saved lazily, so a new graph of + /// objects can be saved in bulk. But note that objects are saved one at a time to ensure Ids. + /// + /// Collection of Lazy to save + /// Optional Id of the User saving the Content + public void Save(IEnumerable> contents, int userId = -1) + { + var e = new SaveEventArgs(); + if (Saving != null) + Saving(contents, e); + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + foreach (var content in contents) + { + SetWriter(content.Value, userId); - SavedCollection.RaiseEvent(new SaveEventArgs>(contents, false), this); + //Only change the publish state if the "previous" version was actually published + if (content.Value.Published) + content.Value.ChangePublishedState(false); + repository.AddOrUpdate(content.Value); + uow.Commit(); + } + } - Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, -1); - } - } + if (Saved != null) + Saved(contents, e); + + Audit.Add(AuditTypes.Save, "Bulk Save (lazy) content performed by user", userId == -1 ? 0 : userId, -1); + } + } /// /// Deletes all content of specified type. All children of deleted content is moved to Recycle Bin. @@ -738,38 +779,45 @@ namespace Umbraco.Core.Services /// Optional Id of the user issueing the delete operation public void DeleteContentOfType(int contentTypeId, int userId = -1) { - //TODO: Do we need another event DeletingContentOfType ? + var uow = _uowProvider.GetUnitOfWork(); + var repository = _repositoryFactory.CreateContentRepository(uow); + //NOTE What about content that has the contenttype as part of its composition? + var query = Query.Builder.Where(x => x.ContentTypeId == contentTypeId); + var contents = repository.GetByQuery(query); - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + var e = new DeleteEventArgs {Id = contentTypeId}; + if (Deleting != null) + Deleting(contents, e); + if (!e.Cancel) { - //NOTE What about content that has the contenttype as part of its composition? - var query = Query.Builder.Where(x => x.ContentTypeId == contentTypeId); - var contents = repository.GetByQuery(query); + foreach (var content in contents.OrderByDescending(x => x.ParentId)) + { + //Look for children of current content and move that to trash before the current content is deleted + var c = content; + var childQuery = Query.Builder.Where(x => x.Path.StartsWith(c.Path)); + var children = repository.GetByQuery(childQuery); - foreach (var content in contents.OrderByDescending(x => x.ParentId)) - { - //Look for children of current content and move that to trash before the current content is deleted - var c = content; - var childQuery = Query.Builder.Where(x => x.Path.StartsWith(c.Path)); - var children = repository.GetByQuery(childQuery); + foreach (var child in children) + { + if (child.ContentType.Id != contentTypeId) + MoveToRecycleBin(child, userId); + } - foreach (var child in children) - { - if (child.ContentType.Id != contentTypeId) - MoveToRecycleBin(child, userId); - } + //Permantly delete the content + Delete(content, userId); + } - //Permantly delete the content, this will raise appropriate events - Delete(content, userId); - } + uow.Dispose(); - Audit.Add(AuditTypes.Delete, - string.Format("Delete Content of Type {0} performed by user", contentTypeId), - userId == -1 ? 0 : userId, -1); + if (Deleted != null) + Deleted(contents, e); + Audit.Add(AuditTypes.Delete, + string.Format("Delete Content of Type {0} performed by user", contentTypeId), + userId == -1 ? 0 : userId, -1); } } - /// + /// /// Permanently deletes an object. /// /// @@ -783,143 +831,143 @@ namespace Umbraco.Core.Services if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(content), this)) return; - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) + if (!e.Cancel) { - //Make sure that published content is unpublished before being deleted - if (HasPublishedVersion(content.Id)) - { - UnPublish(content, userId); - } + //Make sure that published content is unpublished before being deleted + if (HasPublishedVersion(content.Id)) + { + UnPublish(content, userId); + } - //Delete children before deleting the 'possible parent' - var children = GetChildren(content.Id); - foreach (var child in children) - { - Delete(child, userId); - } + //Delete children before deleting the 'possible parent' + var children = GetChildren(content.Id); + foreach (var child in children) + { + Delete(child, userId); + } - SetWriter(content, userId); - repository.Delete(content); - uow.Commit(); + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + SetWriter(content, userId); + repository.Delete(content); + uow.Commit(); + } - Deleted.RaiseEvent(new DeleteEventArgs(content, false), this); + if (Deleted != null) + Deleted(content, e); - Audit.Add(AuditTypes.Delete, "Delete Content performed by user", userId == -1 ? 0 : userId, content.Id); + Audit.Add(AuditTypes.Delete, "Delete Content performed by user", userId == -1 ? 0 : userId, content.Id); } } - /// - /// Permanently deletes versions from an object prior to a specific date. - /// - /// Id of the object to delete versions from - /// Latest version date - /// Optional Id of the User deleting versions of a Content object - public void Delete(IContent content, DateTime versionDate, int userId = -1) - { - Delete(content.Id, versionDate, userId); - } - - /// - /// Permanently deletes specific version(s) from an object. - /// - /// Id of the object to delete a version from - /// Id of the version to delete - /// Boolean indicating whether to delete versions prior to the versionId - /// Optional Id of the User deleting versions of a Content object - public void Delete(IContent content, Guid versionId, bool deletePriorVersions, int userId = -1) - { - Delete(content.Id, versionId, deletePriorVersions, userId); - } - /// /// Permanently deletes versions from an object prior to a specific date. /// /// Id of the object to delete versions from /// Latest version date /// Optional Id of the User deleting versions of a Content object - public void Delete(int id, DateTime versionDate, int userId = -1) + public void DeleteVersions(int id, DateTime versionDate, int userId = -1) { if (DeletingRevisions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, dateToRetain: versionDate), this)) return; - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) { - repository.Delete(id, versionDate); - - DeletedRevisions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, dateToRetain:versionDate), this); - - Audit.Add(AuditTypes.Delete, "Delete Content by version date performed by user", userId == -1 ? 0 : userId, -1); - } - } - - /// - /// Permanently deletes specific version(s) from an object. - /// - /// Id of the object to delete a version from - /// Id of the version to delete - /// Boolean indicating whether to delete versions prior to the versionId - /// Optional Id of the User deleting versions of a Content object - public void Delete(int id, Guid versionId, bool deletePriorVersions, int userId = -1) - { - using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) - { - if (deletePriorVersions) - { - var content = repository.GetByVersion(id, versionId); - Delete(id, content.UpdateDate, userId); + repository.DeleteVersions(id, versionDate); + uow.Commit(); } - if (DeletingRevisions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this)) - return; - - repository.Delete(id, versionId); + if (Deleted != null) + Deleted(versionDate, e); - DeletedRevisions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, specificVersion:versionId), this); - - Audit.Add(AuditTypes.Delete, "Delete Content by version performed by user", userId == -1 ? 0 : userId, -1); + Audit.Add(AuditTypes.Delete, "Delete Content by version date performed by user", userId == -1 ? 0 : userId, -1); } } - /// - /// Deletes an object by moving it to the Recycle Bin - /// - /// Move an item to the Recycle Bin will result in the item being unpublished - /// The to delete - /// Optional Id of the User deleting the Content - public void MoveToRecycleBin(IContent content, int userId = -1) - { + /// + /// Permanently deletes specific version(s) from an object. + /// + /// Id of the object to delete a version from + /// Id of the version to delete + /// Boolean indicating whether to delete versions prior to the versionId + /// Optional Id of the User deleting versions of a Content object + public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = -1) + { + if (deletePriorVersions) + { + var content = GetByVersion(versionId); + DeleteVersions(id, content.UpdateDate, userId); + } + + var e = new DeleteEventArgs {Id = id}; + if (Deleting != null) + Deleting(versionId, e); + + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + repository.DeleteVersion(versionId); + uow.Commit(); + } + + if (Deleted != null) + Deleted(versionId, e); + + Audit.Add(AuditTypes.Delete, "Delete Content by version performed by user", userId == -1 ? 0 : userId, -1); + } + } + + /// + /// Deletes an object by moving it to the Recycle Bin + /// + /// Move an item to the Recycle Bin will result in the item being unpublished + /// The to delete + /// Optional Id of the User deleting the Content + public void MoveToRecycleBin(IContent content, int userId = -1) + { if (Trashing.IsRaisedEventCancelled(new MoveEventArgs(content, -20), this)) return; - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - //Make sure that published content is unpublished before being moved to the Recycle Bin - if (HasPublishedVersion(content.Id)) - { - UnPublish(content, userId); - } + var e = new MoveEventArgs {ParentId = -20}; + if (Trashing != null) + Trashing(content, e); + if (!e.Cancel) + { + //Make sure that published content is unpublished before being moved to the Recycle Bin + if (HasPublishedVersion(content.Id)) + { + UnPublish(content, userId); + } - //Move children to Recycle Bin before the 'possible parent' is moved there - var children = GetChildren(content.Id); - foreach (var child in children) - { - MoveToRecycleBin(child, userId); - } + //Move children to Recycle Bin before the 'possible parent' is moved there + var children = GetChildren(content.Id); + foreach (var child in children) + { + MoveToRecycleBin(child, userId); + } - SetWriter(content, userId); - content.ChangeTrashedState(true); - repository.AddOrUpdate(content); - uow.Commit(); + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + SetWriter(content, userId); + content.ChangeTrashedState(true); + repository.AddOrUpdate(content); + uow.Commit(); + } - Trashed.RaiseEvent(new MoveEventArgs(content, false, -20), this); + if (Trashed != null) + Trashed(content, e); - Audit.Add(AuditTypes.Move, "Move Content to Recycle Bin performed by user", userId == -1 ? 0 : userId, content.Id); - } - } + Audit.Add(AuditTypes.Move, "Move Content to Recycle Bin performed by user", userId == -1 ? 0 : userId, + content.Id); + } + } - /// + /// /// Moves an object to a new location by changing its parent id. /// /// @@ -981,8 +1029,9 @@ namespace Umbraco.Core.Services repository.Delete(content); } uow.Commit(); - Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -20); - } + } + + Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -20); } /// @@ -1099,65 +1148,61 @@ namespace Umbraco.Core.Services if (!e.Cancel) { - // Do some stuff here.. + // Do some stuff here.. RunActionHandlers if (SentToPublish != null) SentToPublish(content, e); Audit.Add(AuditTypes.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id); + + return true; } - - /*SendToPublishEventArgs e = new SendToPublishEventArgs(); - FireBeforeSendToPublish(e); - if (!e.Cancel) - { - global::umbraco.BusinessLogic.Actions.Action.RunActionHandlers(content, ActionToPublish.Instance); - - FireAfterSendToPublish(e); - return true; - } - - return false;*/ return false; } - /// - /// Rollback an object to a previous version. - /// This will create a new version, which is a copy of all the old data. - /// - /// - /// The way data is stored actually only allows us to rollback on properties - /// and not data like Name and Alias of the Content. - /// - /// Id of the being rolled back - /// Id of the version to rollback to - /// Optional Id of the User issueing the rollback of the Content - /// The newly created object - public IContent Rollback(int id, Guid versionId, int userId = -1) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - var content = repository.GetByVersion(id, versionId); + /// + /// Rollback an object to a previous version. + /// This will create a new version, which is a copy of all the old data. + /// + /// + /// The way data is stored actually only allows us to rollback on properties + /// and not data like Name and Alias of the Content. + /// + /// Id of the being rolled back + /// Id of the version to rollback to + /// Optional Id of the User issueing the rollback of the Content + /// The newly created object + public IContent Rollback(int id, Guid versionId, int userId = -1) + { + var e = new RollbackEventArgs(); + var content = GetByVersion(versionId); - if (RollingBack.IsRaisedEventCancelled(new RollbackEventArgs(content), this)) - return content; + if (Rollingback != null) + Rollingback(content, e); - SetUser(content, userId); - SetWriter(content, userId); + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + SetUser(content, userId); + SetWriter(content, userId); - repository.AddOrUpdate(content); - uow.Commit(); + repository.AddOrUpdate(content); + uow.Commit(); + } - RolledBack.RaiseEvent(new RollbackEventArgs(content, false), this); + if (Rolledback != null) + Rolledback(content, e); - Audit.Add(AuditTypes.RollBack, "Content rollback performed by user", content.WriterId, content.Id); + Audit.Add(AuditTypes.RollBack, "Content rollback performed by user", content.WriterId, content.Id); + } - return content; - } - } + return content; + } - /// + #region Internal Methods + /// /// Internal method to set the HttpContextBase for testing. /// /// @@ -1165,6 +1210,35 @@ namespace Umbraco.Core.Services { _httpContext = httpContext; } + #endregion + + #region Private Methods + + /// + /// Gets a flat list of decendents of content from parent id + /// + /// + /// Only contains valid objects, which means + /// that everything in the returned list can be published. + /// If an invalid object is found it will not + /// be added to the list neither will its children. + /// + /// Id of the parent to retrieve children from + /// A list of valid that can be published + private List GetChildrenDeep(int parentId) + { + var list = new List(); + var children = GetChildren(parentId); + foreach (var child in children) + { + if (child.IsValid()) + { + list.Add(child); + list.AddRange(GetChildrenDeep(child.Id)); + } + } + return list; + } /// /// Updates a content object with the User (id), who created the content. @@ -1223,8 +1297,10 @@ namespace Umbraco.Core.Services return _userService != null && (HttpContext.Current != null || _httpContext != null); } - #region Event Handlers - /// + #endregion + + #region Event Handlers + /// /// Occurs before Delete /// public static event TypedEventHandler> Deleting; @@ -1272,7 +1348,11 @@ namespace Umbraco.Core.Services /// /// Occurs after Create /// - public static event TypedEventHandler> Created; + /// + /// Please note that the Content object has been created, but not saved + /// so it does not have an identity yet (meaning no Id has been set). + /// + public static event EventHandler Created; /// /// Occurs before Copy diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 1e3af3e6c0..57ad05f15f 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -4,6 +4,7 @@ using System.Linq; using Umbraco.Core.Auditing; using Umbraco.Core.Events; using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; @@ -97,6 +98,20 @@ namespace Umbraco.Core.Services } } + /// + /// Gets all values for an + /// + /// Id of the to retrieve prevalues from + /// An enumerable list of string values + public IEnumerable GetPreValuesByDataTypeId(int id) + { + var uow = _uowProvider.GetUnitOfWork(); + var dtos = uow.Database.Fetch("WHERE datatypeNodeId = @Id", new {Id = id}); + var list = dtos.Select(x => x.Value).ToList(); + uow.Dispose(); + return list; + } + /// /// Saves an /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 389d859f57..a26302ea97 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -111,30 +111,13 @@ namespace Umbraco.Core.Services /// Optional Id of the user issueing the delete operation void DeleteContentOfType(int contentTypeId, int userId = -1); - /// - /// Permanently deletes versions from an object prior to a specific date. - /// - /// Id of the object to delete versions from - /// Latest version date - /// Optional Id of the User deleting versions of a Content object - void Delete(IContent content, DateTime versionDate, int userId = -1); - - /// - /// Permanently deletes a specific version from an object. - /// - /// Id of the object to delete a version from - /// Id of the version to delete - /// Boolean indicating whether to delete versions prior to the versionId - /// Optional Id of the User deleting versions of a Content object - void Delete(IContent content, Guid versionId, bool deletePriorVersions, int userId = -1); - /// /// Permanently deletes versions from an object prior to a specific date. /// /// Id of the object to delete versions from /// Latest version date /// Optional Id of the User deleting versions of a Content object - void Delete(int id, DateTime versionDate, int userId = -1); + void DeleteVersions(int id, DateTime versionDate, int userId = -1); /// /// Permanently deletes a specific version from an object. @@ -143,7 +126,7 @@ namespace Umbraco.Core.Services /// Id of the version to delete /// Boolean indicating whether to delete versions prior to the versionId /// Optional Id of the User deleting versions of a Content object - void Delete(int id, Guid versionId, bool deletePriorVersions, int userId = -1); + void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = -1); /// /// Deletes an object by moving it to the Recycle Bin @@ -201,10 +184,9 @@ namespace Umbraco.Core.Services /// /// Gets a specific version of an item. /// - /// Id of the to retrieve version from /// Id of the version to retrieve /// An item - IContent GetByIdVersion(int id, Guid versionId); + IContent GetByVersion(Guid versionId); /// /// Gets the published version of an item diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index e0c0be15bc..00c374236c 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -68,5 +68,12 @@ namespace Umbraco.Core.Services /// Id of the DataType control /// IEnumerable GetDataTypeDefinitionByControlId(Guid id); + + /// + /// Gets all values for an + /// + /// Id of the to retrieve prevalues from + /// An enumerable list of string values + IEnumerable GetPreValuesByDataTypeId(int id); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index bbbae59b70..2e240bbce9 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Umbraco.Core.Models; @@ -110,5 +111,57 @@ namespace Umbraco.Core.Services /// Collection of to save /// Id of the User saving the Media void Save(IEnumerable medias, int userId = -1); + + /// + /// Gets an object by its 'UniqueId' + /// + /// Guid key of the Media to retrieve + /// + IMedia GetById(Guid key); + + /// + /// Gets a collection of objects by Level + /// + /// The level to retrieve Media from + /// An Enumerable list of objects + IEnumerable GetByLevel(int level); + + /// + /// Gets a specific version of an item. + /// + /// Id of the version to retrieve + /// An item + IMedia GetByVersion(Guid versionId); + + /// + /// Gets a collection of an objects versions by Id + /// + /// + /// An Enumerable list of objects + IEnumerable GetVersions(int id); + + /// + /// Checks whether an item has any children + /// + /// Id of the + /// True if the media has any children otherwise False + bool HasChildren(int id); + + /// + /// Permanently deletes versions from an object prior to a specific date. + /// + /// Id of the object to delete versions from + /// Latest version date + /// Optional Id of the User deleting versions of a Content object + void DeleteVersions(int id, DateTime versionDate, int userId = -1); + + /// + /// Permanently deletes specific version(s) from an object. + /// + /// Id of the object to delete a version from + /// Id of the version to delete + /// Boolean indicating whether to delete versions prior to the versionId + /// Optional Id of the User deleting versions of a Content object + void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = -1); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index adfecf39aa..3475f60737 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -40,47 +40,53 @@ namespace Umbraco.Core.Services _userService = userService; } - /// - /// Creates an object using the alias of the - /// that this Media is based on. - /// - /// Id of Parent for the new Media item - /// Alias of the - /// Optional id of the user creating the media item - /// - public IMedia CreateMedia(int parentId, string mediaTypeAlias, int userId = -1) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMediaTypeRepository(uow)) - { - var query = Query.Builder.Where(x => x.Alias == mediaTypeAlias); - var mediaTypes = repository.GetByQuery(query); + /// + /// Creates an object using the alias of the + /// that this Media is based on. + /// + /// Id of Parent for the new Media item + /// Alias of the + /// Optional id of the user creating the media item + /// + public IMedia CreateMedia(int parentId, string mediaTypeAlias, int userId = -1) + { + IMediaType mediaType = null; + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMediaTypeRepository(uow)) + { + var query = Query.Builder.Where(x => x.Alias == mediaTypeAlias); + var mediaTypes = repository.GetByQuery(query); - if (!mediaTypes.Any()) - throw new Exception(string.Format("No ContentType matching the passed in Alias: '{0}' was found", mediaTypeAlias)); + if (!mediaTypes.Any()) + throw new Exception(string.Format("No ContentType matching the passed in Alias: '{0}' was found", + mediaTypeAlias)); - var mediaType = mediaTypes.First(); + mediaType = mediaTypes.First(); - if (mediaType == null) - throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null", mediaTypeAlias)); + if (mediaType == null) + throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null", + mediaTypeAlias)); + } - var media = new Models.Media(parentId, mediaType); + var media = new Models.Media(parentId, mediaType); + var e = new NewEventArgs {Alias = mediaTypeAlias, ParentId = parentId}; - if (Creating.IsRaisedEventCancelled(new NewEventArgs(media, mediaTypeAlias, parentId), this)) - return media; - - SetUser(media, userId); + if (Creating != null) + Creating(media, e); + if (!e.Cancel) + { + SetUser(media, userId); - Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parentId), this); + if (Created != null) + Created(media, e); - Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id); + Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id); + } - return media; - } - - } + return media; + } - /// + /// /// Gets an object by Id /// /// Id of the Content to retrieve @@ -94,6 +100,64 @@ namespace Umbraco.Core.Services } } + /// + /// Gets an object by its 'UniqueId' + /// + /// Guid key of the Media to retrieve + /// + public IMedia GetById(Guid key) + { + using (var repository = _repositoryFactory.CreateMediaRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Key == key); + var contents = repository.GetByQuery(query); + return contents.SingleOrDefault(); + } + } + + /// + /// Gets a collection of objects by Level + /// + /// The level to retrieve Media from + /// An Enumerable list of objects + public IEnumerable GetByLevel(int level) + { + using (var repository = _repositoryFactory.CreateMediaRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Level == level); + var contents = repository.GetByQuery(query); + + return contents; + } + } + + /// + /// Gets a specific version of an item. + /// + /// Id of the version to retrieve + /// An item + public IMedia GetByVersion(Guid versionId) + { + using (var repository = _repositoryFactory.CreateMediaRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetByVersion(versionId); + } + } + + /// + /// Gets a collection of an objects versions by Id + /// + /// + /// An Enumerable list of objects + public IEnumerable GetVersions(int id) + { + using (var repository = _repositoryFactory.CreateMediaRepository(_uowProvider.GetUnitOfWork())) + { + var versions = repository.GetAllVersions(id); + return versions; + } + } + /// /// Gets a collection of objects by Parent Id /// @@ -179,6 +243,21 @@ namespace Umbraco.Core.Services } } + /// + /// Checks whether an item has any children + /// + /// Id of the + /// True if the media has any children otherwise False + public bool HasChildren(int id) + { + using (var repository = _repositoryFactory.CreateMediaRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ParentId == id); + int count = repository.Count(query); + return count > 0; + } + } + /// /// Moves an object to a new location /// @@ -198,32 +277,39 @@ namespace Umbraco.Core.Services Audit.Add(AuditTypes.Move, "Move Media performed by user", userId == -1 ? 0 : userId, media.Id); } - /// - /// Deletes an object by moving it to the Recycle Bin - /// - /// The to delete - /// Id of the User deleting the Media - public void MoveToRecycleBin(IMedia media, int userId = -1) - { - //TODO If media item has children those should also be moved to the recycle bin as well - + /// + /// Deletes an object by moving it to the Recycle Bin + /// + /// The to delete + /// Id of the User deleting the Media + public void MoveToRecycleBin(IMedia media, int userId = -1) + { + //TODO If media item has children those should also be moved to the recycle bin as well if (Trashing.IsRaisedEventCancelled(new MoveEventArgs(media, -21), this)) return; - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMediaRepository(uow)) - { - ((Core.Models.Media)media).ChangeTrashedState(true); - repository.AddOrUpdate(media); - uow.Commit(); + var e = new MoveEventArgs {ParentId = -20}; + if (Trashing != null) + Trashing(media, e); + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMediaRepository(uow)) + { + ((Core.Models.Media) media).ChangeTrashedState(true); + repository.AddOrUpdate(media); + uow.Commit(); + } - Trashed.RaiseEvent(new MoveEventArgs(media, false, -21), this); + if (Trashed != null) + Trashed(media, e); - Audit.Add(AuditTypes.Move, "Move Media to Recycle Bin performed by user", userId == -1 ? 0 : userId, media.Id); - } - } + Audit.Add(AuditTypes.Move, "Move Media to Recycle Bin performed by user", userId == -1 ? 0 : userId, + media.Id); + } + } - /// + /// /// Empties the Recycle Bin by deleting all that resides in the bin /// public void EmptyRecycleBin() @@ -239,128 +325,210 @@ namespace Umbraco.Core.Services repository.Delete(content); } uow.Commit(); - - Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -21); } + + Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -20); } - /// - /// Deletes all media of specified type. All children of deleted media is moved to Recycle Bin. - /// - /// This needs extra care and attention as its potentially a dangerous and extensive operation - /// Id of the - /// Optional id of the user deleting the media - public void DeleteMediaOfType(int mediaTypeId, int userId = -1) - { + /// + /// Deletes all media of specified type. All children of deleted media is moved to Recycle Bin. + /// + /// This needs extra care and attention as its potentially a dangerous and extensive operation + /// Id of the + /// Optional id of the user deleting the media + public void DeleteMediaOfType(int mediaTypeId, int userId = -1) + { //TODO: Do we need another event DeletingMediaOfType ? - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMediaRepository(uow)) - { - //NOTE What about media that has the contenttype as part of its composition? - //The ContentType has to be removed from the composition somehow as it would otherwise break - //Dbl.check+test that the ContentType's Id is removed from the ContentType2ContentType table - var query = Query.Builder.Where(x => x.ContentTypeId == mediaTypeId); - var contents = repository.GetByQuery(query); - - //TODO: We shouldn't raise events here, I think we should be doing the same thing as the ContentService and - // performing the move to the recycle bin before deleting the parent content + var uow = _uowProvider.GetUnitOfWork(); + var repository = _repositoryFactory.CreateMediaRepository(uow); + //NOTE What about media that has the contenttype as part of its composition? + //The ContentType has to be removed from the composition somehow as it would otherwise break + //Dbl.check+test that the ContentType's Id is removed from the ContentType2ContentType table + var query = Query.Builder.Where(x => x.ContentTypeId == mediaTypeId); + var contents = repository.GetByQuery(query); + var e = new DeleteEventArgs {Id = mediaTypeId}; + if (Deleting != null) + Deleting(contents, e); //var e = new DeleteEventArgs { Id = mediaTypeId }; //if (Deleting != null) // Deleting(contents, e); - foreach (var content in contents) - { - ((Core.Models.Media)content).ChangeTrashedState(true); - repository.AddOrUpdate(content); - } + if (!e.Cancel) + { + foreach (var content in contents) + { + ((Core.Models.Media) content).ChangeTrashedState(true); + repository.AddOrUpdate(content); + } - uow.Commit(); + uow.Commit(); + uow.Dispose(); - Audit.Add(AuditTypes.Delete, "Delete Media items by Type performed by user", userId == -1 ? 0 : userId, -1); - } - } + if (Deleted != null) + Deleted(contents, e); + Audit.Add(AuditTypes.Delete, "Delete Media items by Type performed by user", userId == -1 ? 0 : userId, -1); + } + } - /// - /// Permanently deletes an object - /// - /// - /// Please note that this method will completely remove the Media from the database, - /// but current not from the file system. - /// - /// The to delete - /// Id of the User deleting the Media - public void Delete(IMedia media, int userId = -1) - { + /// + /// Permanently deletes an object + /// + /// + /// Please note that this method will completely remove the Media from the database, + /// but current not from the file system. + /// + /// The to delete + /// Id of the User deleting the Media + public void Delete(IMedia media, int userId = -1) + { if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(media), this)) return; - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMediaRepository(uow)) - { - repository.Delete(media); - uow.Commit(); + var e = new DeleteEventArgs {Id = media.Id}; + if (Deleting != null) + Deleting(media, e); + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMediaRepository(uow)) + { + repository.Delete(media); + uow.Commit(); + } - Deleted.RaiseEvent(new DeleteEventArgs(media, false), this); + if (Deleted != null) + Deleted(media, e); - Audit.Add(AuditTypes.Delete, "Delete Media performed by user", userId == -1 ? 0 : userId, media.Id); - } - } + Audit.Add(AuditTypes.Delete, "Delete Media performed by user", userId == -1 ? 0 : userId, media.Id); + } + } - /// - /// Saves a single object - /// - /// The to save - /// Id of the User saving the Content - public void Save(IMedia media, int userId = -1) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMediaRepository(uow)) - { + /// + /// Permanently deletes versions from an object prior to a specific date. + /// + /// Id of the object to delete versions from + /// Latest version date + /// Optional Id of the User deleting versions of a Content object + public void DeleteVersions(int id, DateTime versionDate, int userId = -1) + { + var e = new DeleteEventArgs { Id = id }; + if (Deleting != null) + Deleting(versionDate, e); - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(media), this)) - return; + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMediaRepository(uow)) + { + repository.DeleteVersions(id, versionDate); + uow.Commit(); + } - SetUser(media, userId); - repository.AddOrUpdate(media); - uow.Commit(); + if (Deleted != null) + Deleted(versionDate, e); - Saved.RaiseEvent(new SaveEventArgs(media, false), this); + Audit.Add(AuditTypes.Delete, "Delete Media by version date performed by user", userId == -1 ? 0 : userId, -1); + } + } - Audit.Add(AuditTypes.Save, "Save Media performed by user", media.CreatorId, media.Id); - } - } + /// + /// Permanently deletes specific version(s) from an object. + /// + /// Id of the object to delete a version from + /// Id of the version to delete + /// Boolean indicating whether to delete versions prior to the versionId + /// Optional Id of the User deleting versions of a Content object + public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = -1) + { + if (deletePriorVersions) + { + var content = GetByVersion(versionId); + DeleteVersions(id, content.UpdateDate, userId); + } - /// - /// Saves a collection of objects - /// - /// Collection of to save - /// Id of the User saving the Content - public void Save(IEnumerable medias, int userId = -1) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMediaRepository(uow)) - { - if (SavingCollection.IsRaisedEventCancelled(new SaveEventArgs>(medias), this)) - return; + var e = new DeleteEventArgs { Id = id }; + if (Deleting != null) + Deleting(versionId, e); - foreach (var media in medias) - { - SetUser(media, userId); - repository.AddOrUpdate(media); - } + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMediaRepository(uow)) + { + repository.DeleteVersion(versionId); + uow.Commit(); + } + + if (Deleted != null) + Deleted(versionId, e); + + Audit.Add(AuditTypes.Delete, "Delete Media by version performed by user", userId == -1 ? 0 : userId, -1); + } + } + + /// + /// Saves a single object + /// + /// The to save + /// Id of the User saving the Content + public void Save(IMedia media, int userId = -1) + { + var e = new SaveEventArgs(); + if (Saving != null) + Saving(media, e); + + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMediaRepository(uow)) + { + SetUser(media, userId); + repository.AddOrUpdate(media); + uow.Commit(); + } + + if (Saved != null) + Saved(media, e); + } + Audit.Add(AuditTypes.Save, "Save Media performed by user", media.CreatorId, media.Id); + } + + /// + /// Saves a collection of objects + /// + /// Collection of to save + /// Id of the User saving the Content + public void Save(IEnumerable medias, int userId = -1) + { + var e = new SaveEventArgs(); + if (Saving != null) + Saving(medias, e); + + if (!e.Cancel) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMediaRepository(uow)) + { + foreach (var media in medias) + { + SetUser(media, userId); + repository.AddOrUpdate(media); + } //commit the whole lot in one go - uow.Commit(); + uow.Commit(); + } - SavedCollection.RaiseEvent(new SaveEventArgs>(medias, false), this); + if (Saved != null) + Saved(medias, e); - Audit.Add(AuditTypes.Save, "Save Media items performed by user", userId == -1 ? 0 : userId, -1); - } - } + Audit.Add(AuditTypes.Save, "Save Media items performed by user", userId == -1 ? 0 : userId, -1); + } + } - /// + /// /// Internal method to set the HttpContextBase for testing. /// /// @@ -439,7 +607,10 @@ namespace Umbraco.Core.Services /// /// Occurs after Create /// - public static event TypedEventHandler> Created; + /// + /// Please note that the Media object has been created, but not saved + /// so it does not have an identity yet (meaning no Id has been set). + /// /// /// Occurs before Content is moved to Recycle Bin diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a9db9c72f6..b768e420e4 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -54,6 +54,7 @@ False ..\..\lib\SQLCE4\System.Data.SqlServerCe.Entity.dll + @@ -167,8 +168,13 @@ + + + + + @@ -379,6 +385,7 @@ + @@ -398,15 +405,11 @@ + - - - - - diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 576d6ba96c..9948282c10 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -192,7 +192,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert Assert.That(contentType3.PropertyTypes.Any(x => x.Alias == "metaDescription"), Is.False); Assert.That(contentType.PropertyGroups.Count, Is.EqualTo(contentType3.PropertyGroups.Count)); - Assert.That(contentType.PropertyTypes.Count(), Is.GreaterThan(contentType3.PropertyTypes.Count())); + Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(contentType3.PropertyTypes.Count())); } [Test] diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs index f455a33421..a39dba0986 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -189,7 +189,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert Assert.That(mediaTypeV3.PropertyTypes.Any(x => x.Alias == "title"), Is.False); Assert.That(mediaType.PropertyGroups.Count, Is.EqualTo(mediaTypeV3.PropertyGroups.Count)); - Assert.That(mediaType.PropertyTypes.Count(), Is.GreaterThan(mediaTypeV3.PropertyTypes.Count())); + Assert.That(mediaType.PropertyTypes.Count(), Is.EqualTo(mediaTypeV3.PropertyTypes.Count())); } [Test] diff --git a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs index c1f0dd1d95..1cd04b024e 100644 --- a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs @@ -64,6 +64,8 @@ namespace Umbraco.Tests.Persistence { AppDomain.CurrentDomain.SetData("DataDirectory", null); + SyntaxConfig.SqlSyntaxProvider = null; + //reset the app context ApplicationContext.Current = null; Resolution.IsFrozen = false; diff --git a/src/Umbraco.Tests/Persistence/SqlTableByTableTest.cs b/src/Umbraco.Tests/Persistence/SqlTableByTableTest.cs index 0512b8bea7..82097c487e 100644 --- a/src/Umbraco.Tests/Persistence/SqlTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/SqlTableByTableTest.cs @@ -51,6 +51,8 @@ namespace Umbraco.Tests.Persistence { AppDomain.CurrentDomain.SetData("DataDirectory", null); + SyntaxConfig.SqlSyntaxProvider = null; + //reset the app context ApplicationContext.Current = null; Resolution.IsFrozen = false; diff --git a/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlSyntaxProviderTests.cs b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlSyntaxProviderTests.cs new file mode 100644 index 0000000000..6172c9d4f7 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlSyntaxProviderTests.cs @@ -0,0 +1,46 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Tests.Persistence.SyntaxProvider +{ + [TestFixture] + public class SqlSyntaxProviderTests + { + [SetUp] + public void SetUp() + { + SyntaxConfig.SqlSyntaxProvider = SqlCeSyntaxProvider.Instance; + } + + [Test] + public void Can_Generate_Create_Table_Statement() + { + var type = typeof (NodeDto); + var definition = DefinitionFactory.GetTableDefinition(type); + + string create = SyntaxConfig.SqlSyntaxProvider.Format(definition); + string primaryKey = SyntaxConfig.SqlSyntaxProvider.FormatPrimaryKey(definition); + var indexes = SyntaxConfig.SqlSyntaxProvider.Format(definition.Indexes); + var keys = SyntaxConfig.SqlSyntaxProvider.Format(definition.ForeignKeys); + + Console.WriteLine(create); + Console.WriteLine(primaryKey); + foreach (var sql in keys) + { + Console.WriteLine(sql); + } + + foreach (var sql in indexes) + { + Console.WriteLine(sql); + } + } + + [TearDown] + public void TearDown() + {} + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9c96735e8b..efe31df9fe 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -166,6 +166,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index a7faf4ab0b..3b496ed522 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -407,6 +407,7 @@ + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 5ed73be750..6796e19173 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -23,6 +23,7 @@ Notifications Public access Publish + Unpublish Reload nodes Republish entire site Permissions @@ -779,6 +780,7 @@ To manage your website, simply open the umbraco back office and start adding con Xslt could not be saved, check file permissions Xslt saved No errors in xslt + Content unpublished Uses CSS syntax ex: h1, .redHeader, .blueTex diff --git a/src/Umbraco.Web.UI/umbraco/dashboard/images/access-denied.png b/src/Umbraco.Web.UI/umbraco/dashboard/images/access-denied.png new file mode 100644 index 0000000000..63a3ea6d5d Binary files /dev/null and b/src/Umbraco.Web.UI/umbraco/dashboard/images/access-denied.png differ diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs index d419191f5b..462f8915c9 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs @@ -203,6 +203,8 @@ namespace Umbraco.Web.Routing // expected content anymore and the alternate template does not apply. _publishedContentRequest.AlternateTemplateAlias = _umbracoContext.HttpContext.Request["altTemplate"]; + _umbracoContext.HttpContext.Trace.Write(string.Format("test {0}", _publishedContentRequest.AlternateTemplateAlias)); + // handle "not found", follow internal redirects, validate access, template // because these might loop, we have to have some sort of infinite loop detection int i = 0, j = 0; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs index 9729e27c01..52a476ed09 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs @@ -161,7 +161,7 @@ namespace umbraco.controls SaveAllowedChildTypes(); NodeTypeEditorCtrl.Save(); - prnt.speechBubble( BasePages.BasePage.speechBubbleIcon.save, ui.Text("speechBubbles", "contentTypeSavedHeader", null),"" + xtra); + prnt.ClientTools.ShowSpeechBubble( BasePages.BasePage.speechBubbleIcon.save, ui.Text("speechBubbles", "contentTypeSavedHeader", null),"" + xtra); } private void SaveAllowedChildTypes() { @@ -284,7 +284,7 @@ namespace umbraco.controls LoadExistingTabsOnTabsPane(); populateTabDDLs(); - prnt.speechBubble(BasePages.BasePage.speechBubbleIcon.info,ui.Text("speechBubbles", "contentTypeTabDeleted", null), ui.Text("speechBubbles", "contentTypeTabDeletedText", b.ID, null)); + prnt.ClientTools.ShowSpeechBubble(BasePages.BasePage.speechBubbleIcon.info, ui.Text("speechBubbles", "contentTypeTabDeleted", null), ui.Text("speechBubbles", "contentTypeTabDeletedText", b.ID, null)); } @@ -411,7 +411,7 @@ namespace umbraco.controls if (NameTxt.Text.Trim() != "" && AliasTxt.Text.Trim() != "") { string[] info = {NameTxt.Text, DataTypeDDL.SelectedItem.Value}; - ctctrl.prnt.speechBubble(BasePages.BasePage.speechBubbleIcon.info,ui.Text("speechBubbles", "contentTypePropertyTypeCreated", null), ui.Text("speechBubbles", "contentTypePropertyTypeCreatedText", info, null)); + ctctrl.prnt.ClientTools.ShowSpeechBubble(BasePages.BasePage.speechBubbleIcon.info, ui.Text("speechBubbles", "contentTypePropertyTypeCreated", null), ui.Text("speechBubbles", "contentTypePropertyTypeCreatedText", info, null)); _dt.AddPropertyType(cms.businesslogic.datatype.DataTypeDefinition.GetDataTypeDefinition(int.Parse(DataTypeDDL.SelectedValue)),AliasTxt.Text,NameTxt.Text); if (int.Parse(TabDDL.SelectedValue) != 0) @@ -428,7 +428,7 @@ namespace umbraco.controls else { - ctctrl.prnt.speechBubble(BasePages.BasePage.speechBubbleIcon.error,ui.Text("error"), ui.Text("errors", "contentTypeAliasAndNameNotNull", null)); + ctctrl.prnt.ClientTools.ShowSpeechBubble(BasePages.BasePage.speechBubbleIcon.error, ui.Text("error"), ui.Text("errors", "contentTypeAliasAndNameNotNull", null)); } } } @@ -552,7 +552,7 @@ namespace umbraco.controls ent.loadGenericPropertyTypesOnPane(); BasePages.BasePage bp = (BasePages.BasePage) this.Page; - bp.speechBubble(BasePages.BasePage.speechBubbleIcon.info ,ui.Text("speechBubbles", "contentTypePropertyTypeDeleted", null),""); + bp.ClientTools.ShowSpeechBubble(BasePages.BasePage.speechBubbleIcon.info, ui.Text("speechBubbles", "contentTypePropertyTypeDeleted", null), ""); ent.OnPropertyTypeDelete(this, new System.EventArgs()); } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs index b583a7cd76..61963f65c3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs @@ -110,7 +110,7 @@ namespace umbraco.cms.presentation.developer System.Web.HttpRuntime.Cache.Remove(string.Format("UmbracoDataTypeDefinition{0}", dt.UniqueId)); - this.speechBubble(BasePages.BasePage.speechBubbleIcon.save, ui.Text("speechBubbles", "dataTypeSaved", null), ""); + ClientTools.ShowSpeechBubble(BasePages.BasePage.speechBubbleIcon.save, ui.Text("speechBubbles", "dataTypeSaved", null), ""); //Response.Redirect("editDataType.aspx?id=" + _id); } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs index fe8864594e..8e6ea70c22 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs @@ -115,7 +115,7 @@ namespace umbraco.cms.presentation.developer else macro.GetMacro(macroID).removeFromCache(); - base.speechBubble(speechBubbleIcon.save, "Macro saved", ""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, "Macro saved", ""); // Check for assemblyBrowser diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs index 73e3a172ba..82a3b98935 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs @@ -362,6 +362,9 @@ namespace umbraco.cms.presentation library.UnPublishSingleNode(_document.Id); + Current.ClientTools.SyncTree(_document.Path, true); + ClientTools.ShowSpeechBubble(speechBubbleIcon.success, ui.Text("unpublish"), ui.Text("speechBubbles", "contentUnpublished")); + //newPublishStatus.Text = "0"; } @@ -430,7 +433,7 @@ namespace umbraco.cms.presentation private void ShowUserValidationError(string message) { this.Controls.Clear(); - this.Controls.Add(new LiteralControl(String.Format("

{0}

", message))); + this.Controls.Add(new LiteralControl(String.Format("

Access denied

Access denied{0}
", message))); } /// @@ -442,18 +445,19 @@ namespace umbraco.cms.presentation // Validate permissions if (!base.ValidateUserApp("content")) { - ShowUserValidationError("The current user doesn't have access to this application. Please contact the system administrator."); + ShowUserValidationError("

The current user doesn't have access to this application

Please contact the system administrator if you think that you should have access.

"); return false; } if (!base.ValidateUserNodeTreePermissions(_document.Path, ActionBrowse.Instance.Letter.ToString())) { - ShowUserValidationError("The current user doesn't have permissions to browse this document. Please contact the system administrator."); + ShowUserValidationError( + "

The current user doesn't have permissions to browse this document

Please contact the system administrator if you think that you should have access.

"); return false; } //TODO: Change this, when we add view capabilities, the user will be able to view but not edit! if (!base.ValidateUserNodeTreePermissions(_document.Path, ActionUpdate.Instance.Letter.ToString())) { - ShowUserValidationError("The current user doesn't have permissions to edit this document. Please contact the system administrator."); + ShowUserValidationError("

The current user doesn't have permissions to edit this document

Please contact the system administrator if you think that you should have access.

"); return false; } return true; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs index af928011d7..fc02bd5c9d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs @@ -194,7 +194,7 @@ namespace umbraco.cms.presentation.members } - this.speechBubble(BasePages.BasePage.speechBubbleIcon.save, + ClientTools.ShowSpeechBubble(BasePages.BasePage.speechBubbleIcon.save, ui.Text("speechBubbles", "editMemberSaved", base.getUser()), ""); } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs index 20b406281d..873a33a892 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs @@ -93,7 +93,7 @@ namespace umbraco.settings currentItem.setValue(int.Parse(t.ID),t.Text); } } - speechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"),""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), ""); } #region Web Form Designer generated code override protected void OnInit(EventArgs e) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx.cs index 074b08fa51..af41d761ba 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx.cs @@ -71,7 +71,7 @@ namespace umbraco.settings currentLanguage.Save(); updateCultureList(); - speechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "languageSaved"), ""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "languageSaved"), ""); } #region Web Form Designer generated code diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx.cs index c7722fbb93..246bd866cc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx.cs @@ -73,7 +73,7 @@ namespace umbraco.cms.presentation.settings.stylesheet stylesheetproperty.StyleSheet().saveCssToFile(); } catch {} - this.speechBubble(speechBubbleIcon.save,ui.Text("speechBubbles", "editStylesheetPropertySaved", base.getUser()),""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editStylesheetPropertySaved", base.getUser()), ""); setupPreView(); stylesheetproperty.Save(); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs index c61fa281ac..0ef2ec1d94 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs @@ -501,17 +501,17 @@ namespace umbraco.cms.presentation.user } - speechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editUserSaved", base.getUser()), ""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editUserSaved", base.getUser()), ""); } catch (Exception ex) { - speechBubble(speechBubbleIcon.error, ui.Text("speechBubbles", "editUserError", base.getUser()), ""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.error, ui.Text("speechBubbles", "editUserError", base.getUser()), ""); Log.Add(LogTypes.Error, 0, ex.Message); } } else { - speechBubble(speechBubbleIcon.error, ui.Text("speechBubbles", "editUserError", base.getUser()), ""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.error, ui.Text("speechBubbles", "editUserError", base.getUser()), ""); } } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.cs index c784a9bebe..3211033f7d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.cs @@ -70,7 +70,7 @@ namespace umbraco.cms.presentation.user userType.DefaultPermissions = actions; userType.Save(); - speechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editUserTypeSaved", base.getUser()), ""); + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editUserTypeSaved", base.getUser()), ""); } protected List CurrentUserTypeActions diff --git a/src/umbraco.businesslogic/ApplicationTree.cs b/src/umbraco.businesslogic/ApplicationTree.cs index d5a4ff4ffa..67faf9b7ec 100644 --- a/src/umbraco.businesslogic/ApplicationTree.cs +++ b/src/umbraco.businesslogic/ApplicationTree.cs @@ -260,6 +260,10 @@ namespace umbraco.BusinessLogic LoadXml(doc => { + var el = doc.Root.Elements("add").SingleOrDefault(x => x.Attribute("alias").Value == alias && x.Attribute("application").Value == applicationAlias); + + if (el == null) + { doc.Root.Add(new XElement("add", new XAttribute("silent", silent), new XAttribute("initialize", initialize), @@ -272,6 +276,7 @@ namespace umbraco.BusinessLogic new XAttribute("assembly", assemblyName), new XAttribute("type", type), new XAttribute("action", string.IsNullOrEmpty(action) ? "" : action))); + } }, true); } diff --git a/src/umbraco.businesslogic/Log.cs b/src/umbraco.businesslogic/Log.cs index 86f38da8ce..89ccc31587 100644 --- a/src/umbraco.businesslogic/Log.cs +++ b/src/umbraco.businesslogic/Log.cs @@ -86,8 +86,7 @@ namespace umbraco.BusinessLogic if (UmbracoSettings.DisabledLogTypes != null && UmbracoSettings.DisabledLogTypes.SelectSingleNode(String.Format("//logTypeAlias [. = '{0}']", type.ToString().ToLower())) == null) { - - if (comment.Length > 3999) + if (comment != null && comment.Length > 3999) comment = comment.Substring(0, 3955) + "..."; if (UmbracoSettings.EnableAsyncLogging)