diff --git a/.hgignore b/.hgignore index 250bf807ce..ee97264e54 100644 --- a/.hgignore +++ b/.hgignore @@ -4,7 +4,6 @@ web.Template.[a-zA-Z0-9]*.Debug.config syntax: glob *.obj -*.exe *.pdb *.user *.aps @@ -63,6 +62,7 @@ src/Umbraco.Web.UI/Views/*.vbhtml src/Umbraco.Tests/config/umbracoSettings.config src/Umbraco.Web.UI/App_Plugins/* src/Umbraco.Web.UI/Views/* +src/packages/ -src\Umbraco.Web.UI\[Ww]eb.config -*.transformed +src\Umbraco.Web.UI\[W]eb.config +*.transformed \ No newline at end of file diff --git a/.hgtags b/.hgtags index 6daa26e240..6a3fe90af2 100644 --- a/.hgtags +++ b/.hgtags @@ -16,3 +16,11 @@ f6da531fbb4c251ff61d314e2a7effb13c71e74a Release-4.10.0 7f827760cb49d749616859f528d19dde64807947 Release-4.11.1 7f827760cb49d749616859f528d19dde64807947 Release-4.11.1 6eb5f2fb5a88aa29ea544499df9160a1b31b5638 Release-4.11.1 +73711b806a4044dbf0d7d7c095ddfdda23b09932 4.11.2 +73711b806a4044dbf0d7d7c095ddfdda23b09932 4.11.2 +0000000000000000000000000000000000000000 4.11.2 +73711b806a4044dbf0d7d7c095ddfdda23b09932 Release-4.11.2 +77db220b89e166b3c0736ed321ecfd6416cddbcd Release-4.11.2.1 +54cde33b809dcb3a1f7e7ae0d5375f6dd0d89c8d Release-4.11.2.2 +ff3bb24ea0c915878396a6ae27f1ff164e8ac150 Release-6.0.0-beta +56015ac26f5ab60e3c61b1d09075297b660afa07 Release-6.0.0-RC diff --git a/build/Build.NuGet.bat b/build/Build.NuGet.bat deleted file mode 100644 index b38edf0762..0000000000 --- a/build/Build.NuGet.bat +++ /dev/null @@ -1,11 +0,0 @@ -@ECHO OFF -set version=6.0.0.4 -..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% -..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.nuspec -Version %version% - -if ERRORLEVEL 1 goto :showerror - -goto :EOF - -:showerror -pause \ No newline at end of file diff --git a/build/Build.Plus.NuGet.bat b/build/Build.Plus.NuGet.bat deleted file mode 100644 index fb8cad70a5..0000000000 --- a/build/Build.Plus.NuGet.bat +++ /dev/null @@ -1,13 +0,0 @@ -@ECHO OFF -set version=6.0.0.5 -%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_NUMBER=%version% -..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% -..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.nuspec -Version %version% - - -if ERRORLEVEL 1 goto :showerror - -goto :EOF - -:showerror -pause \ No newline at end of file diff --git a/build/Build.bat b/build/Build.bat index aca687fdcc..525f3cf41b 100644 --- a/build/Build.bat +++ b/build/Build.bat @@ -1,9 +1,29 @@ @ECHO OFF -%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" +SET release=6.0.0 +SET comment=RC +SET version=%release% -if ERRORLEVEL 1 goto :showerror +IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%) -goto :EOF +%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% + +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Code\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Data\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Plugins\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\css\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\macroScripts\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\masterpages\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\media\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\scripts\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\usercontrols\dummy.txt +echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\xslt\dummy.txt + +..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% +..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.nuspec -Version %version% + +IF ERRORLEVEL 1 GOTO :showerror + +GOTO :EOF :showerror -pause \ No newline at end of file +PAUSE \ No newline at end of file diff --git a/build/Build.proj b/build/Build.proj index 71376d209e..e5c0116e15 100644 --- a/build/Build.proj +++ b/build/Build.proj @@ -23,6 +23,12 @@ .$(BUILD_NUMBER) + + + .$(BUILD_RELEASE) + + + .$(BUILD_RELEASE)-$(BUILD_COMMENT) @@ -141,7 +147,7 @@ - + @@ -152,7 +158,20 @@ SkipUnchangedFiles="false" /> - + + + + + + + + + + + @@ -161,5 +180,16 @@ - + + + + + + + \ No newline at end of file diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index a42f103856..90e25d6b3f 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -22,16 +22,22 @@ + + + + + + @@ -43,18 +49,24 @@ + + + + + + + + - - \ No newline at end of file diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index a62a9c1de8..b7e9ba8100 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -19,7 +19,9 @@ - + + + diff --git a/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj index ca9bd06eb3..db3caf4b82 100644 --- a/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj +++ b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj @@ -39,6 +39,7 @@ TRACE prompt 4 + bin\Release\SQLCE4Umbraco.XML diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 9e721820c2..60bf0ee45a 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Reflection; namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version(6, 0, 0); + private static readonly Version Version = new Version("6.0.0"); /// /// Gets the current version of Umbraco. @@ -23,7 +23,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return ""; } } + public static string CurrentComment { get { return "RC"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 53bf71444e..eda2cf494c 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -7,10 +7,13 @@ using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.Migrations; +using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Publishing; using Umbraco.Core.Services; +using MigrationsVersionFourNineZero = Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero; namespace Umbraco.Core { @@ -40,7 +43,7 @@ namespace Umbraco.Core //create database and service contexts for the app context var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName); - UmbracoDatabase.Mapper = new PetaPocoMapper(); + Database.Mapper = new PetaPocoMapper(); var dbContext = new DatabaseContext(dbFactory); var serviceContext = new ServiceContext( new PetaPocoUnitOfWorkProvider(dbFactory), @@ -132,12 +135,33 @@ namespace Umbraco.Core MacroPropertyTypeResolver.Current = new MacroPropertyTypeResolver( PluginManager.Current.ResolveMacroPropertyTypes()); + //TODO: Y U NO WORK? + //MigrationResolver.Current = new MigrationResolver( + // PluginManager.Current.ResolveMigrationTypes()); + + //the database migration objects + MigrationResolver.Current = new MigrationResolver(new List + { + typeof (MigrationsVersionFourNineZero.RemoveUmbracoAppConstraints), + typeof (DeleteAppTables), + typeof (EnsureAppsTreesUpdated), + typeof (MoveMasterContentTypeData), + typeof (NewCmsContentType2ContentTypeTable), + typeof (RemoveMasterContentTypeColumn), + typeof (RenameCmsTabTable), + typeof (RenameTabIdColumn), + typeof (UpdateCmsContentTypeAllowedContentTypeTable), + typeof (UpdateCmsContentTypeTable), + typeof (UpdateCmsContentVersionTable), + typeof (UpdateCmsPropertyTypeGroupTable) + }); + PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver( PluginManager.Current.ResolvePropertyEditorValueConverters()); //add the internal ones, these are not public currently so need to add them manually PropertyEditorValueConvertersResolver.Current.AddType(); PropertyEditorValueConvertersResolver.Current.AddType(); PropertyEditorValueConvertersResolver.Current.AddType(); - } + } } } diff --git a/src/Umbraco.Core/DictionaryExtensions.cs b/src/Umbraco.Core/DictionaryExtensions.cs index 7e09b519a8..83d89fcaba 100644 --- a/src/Umbraco.Core/DictionaryExtensions.cs +++ b/src/Umbraco.Core/DictionaryExtensions.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using System.Linq; using System.Text; using System.Web.Mvc; +using System.Web; namespace Umbraco.Core { @@ -158,7 +159,7 @@ namespace Umbraco.Core var builder = new StringBuilder(); foreach (var i in d) { - builder.Append(String.Format("{0}={1}&", i.Key, i.Value)); + builder.Append(String.Format("{0}={1}&", HttpUtility.UrlEncode(i.Key), i.Value == null ? string.Empty : HttpUtility.UrlEncode(i.Value.ToString()))); } return builder.ToString().TrimEnd('&'); } diff --git a/src/Umbraco.Core/Dynamics/DynamicXml.cs b/src/Umbraco.Core/Dynamics/DynamicXml.cs index a6a7441580..f0942d02a8 100644 --- a/src/Umbraco.Core/Dynamics/DynamicXml.cs +++ b/src/Umbraco.Core/Dynamics/DynamicXml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Dynamic; using System.Reflection; @@ -11,7 +12,8 @@ using System.Web; namespace Umbraco.Core.Dynamics { - public class DynamicXml : DynamicObject, IEnumerable, IEnumerable + [TypeConverter(typeof(DynamicXmlConverter))] + public class DynamicXml : DynamicObject, IEnumerable, IEnumerable { public XElement BaseElement { get; set; } @@ -213,6 +215,16 @@ namespace Umbraco.Core.Dynamics } return root; } + + /// + /// Return the string version of Xml + /// + /// + public override string ToString() + { + return ToXml(); + } + public IHtmlString ToHtml() { return new HtmlString(this.ToXml()); diff --git a/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs b/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs new file mode 100644 index 0000000000..2c3f597cd3 --- /dev/null +++ b/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + +namespace Umbraco.Core.Dynamics +{ + /// + /// A custom type converter for DynamicXml + /// + public class DynamicXmlConverter : TypeConverter + { + public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType) + { + var convertableTypes = new[] {typeof(string), typeof(XElement), typeof(XmlElement), typeof(XmlDocument)}; + + return convertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, sourceType)) + || base.CanConvertFrom(context, sourceType); + } + + public override object ConvertTo( + ITypeDescriptorContext context, + CultureInfo culture, + object value, + Type destinationType) + { + var dxml = value as DynamicXml; + if (dxml == null) + return null; + //string + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + return value.ToString(); + } + //XElement + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + return dxml.BaseElement; + } + //XmlElement + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + var xDoc = new XmlDocument(); + xDoc.LoadXml(dxml.ToString()); + return xDoc.DocumentElement; + } + //XmlDocument + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + var xDoc = new XmlDocument(); + xDoc.LoadXml(dxml.ToString()); + return xDoc; + } + return base.ConvertTo(context, culture, value, destinationType); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/MigrationEventArgs.cs b/src/Umbraco.Core/Events/MigrationEventArgs.cs new file mode 100644 index 0000000000..c447ebcd1a --- /dev/null +++ b/src/Umbraco.Core/Events/MigrationEventArgs.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Persistence.Migrations; + +namespace Umbraco.Core.Events +{ + public class MigrationEventArgs : CancellableObjectEventArgs> + { + /// + /// Constructor accepting multiple migrations that are used in the migration runner + /// + /// + /// + /// + /// + public MigrationEventArgs(IEnumerable eventObject, Version configuredVersion, Version targetVersion, bool canCancel) + : base(eventObject, canCancel) + { + ConfiguredVersion = configuredVersion; + TargetVersion = targetVersion; + } + + /// + /// Constructor accepting multiple migrations that are used in the migration runner + /// + /// + /// + /// + /// + /// + internal MigrationEventArgs(IEnumerable eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel) + : base(eventObject, canCancel) + { + MigrationContext = migrationContext; + ConfiguredVersion = configuredVersion; + TargetVersion = targetVersion; + } + + /// + /// Constructor accepting multiple migrations that are used in the migration runner + /// + /// + /// + /// + public MigrationEventArgs(IEnumerable eventObject, Version configuredVersion, Version targetVersion) + : base(eventObject) + { + ConfiguredVersion = configuredVersion; + TargetVersion = targetVersion; + } + + /// + /// Returns all migrations that were used in the migration runner + /// + public IEnumerable Migrations + { + get { return EventObject; } + } + + public Version ConfiguredVersion { get; private set; } + + public Version TargetVersion { get; private set; } + + internal MigrationContext MigrationContext { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index 49128db5b2..fa7bbbcdb7 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -1,11 +1,12 @@ -using Umbraco.Core.CodeAnnotations; +using System.IO; +using Umbraco.Core.CodeAnnotations; namespace Umbraco.Core.IO { [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] public static class FileSystemExtensions { - [UmbracoExperimentalFeature("", "Will be declared public after 4.10")] + [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] internal static long GetSize(this IFileSystem fs, string path) { var s = fs.OpenFile(path); @@ -15,10 +16,22 @@ namespace Umbraco.Core.IO return size; } - [UmbracoExperimentalFeature("", "Will be declared public after 4.10")] + [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] internal static void CopyFile(this IFileSystem fs, string path, string newPath) { fs.AddFile(newPath, fs.OpenFile(path)); } + + [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] + internal static string GetExtension(this IFileSystem fs, string path) + { + return Path.GetExtension(fs.GetFullPath(path)); + } + + [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] + internal static string GetFileName(this IFileSystem fs, string path) + { + return Path.GetFileName(fs.GetFullPath(path)); + } } } diff --git a/src/Umbraco.Core/IO/FileSystemWrapper.cs b/src/Umbraco.Core/IO/FileSystemWrapper.cs index 236a79a380..924485db43 100644 --- a/src/Umbraco.Core/IO/FileSystemWrapper.cs +++ b/src/Umbraco.Core/IO/FileSystemWrapper.cs @@ -15,8 +15,7 @@ namespace Umbraco.Core.IO /// /// This abstract class just wraps the 'real' IFileSystem object passed in to its constructor. /// - [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] - internal abstract class FileSystemWrapper : IFileSystem + public abstract class FileSystemWrapper : IFileSystem { private readonly IFileSystem _wrapped; diff --git a/src/Umbraco.Core/IO/IFileSystem.cs b/src/Umbraco.Core/IO/IFileSystem.cs index 2fca65c652..8e1753da6d 100644 --- a/src/Umbraco.Core/IO/IFileSystem.cs +++ b/src/Umbraco.Core/IO/IFileSystem.cs @@ -5,8 +5,7 @@ using Umbraco.Core.CodeAnnotations; namespace Umbraco.Core.IO { - [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] - internal interface IFileSystem + public interface IFileSystem { IEnumerable GetDirectories(string path); diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index 0193f7480a..e3d8f489c6 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -11,8 +11,7 @@ namespace Umbraco.Core.IO /// A custom file system provider for media /// [FileSystemProvider("media")] - [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] - internal class MediaFileSystem : FileSystemWrapper + public class MediaFileSystem : FileSystemWrapper { public MediaFileSystem(IFileSystem wrapped) : base(wrapped) diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 4f3262b560..74d7f27671 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -10,16 +10,19 @@ using Umbraco.Core.Publishing; namespace Umbraco.Core.IO { - [UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")] - internal class PhysicalFileSystem : IFileSystem + public class PhysicalFileSystem : IFileSystem { - private readonly string _rootPath; + internal string RootPath { get; private set; } private readonly string _rootUrl; public PhysicalFileSystem(string virtualRoot) { - _rootPath = System.Web.Hosting.HostingEnvironment.MapPath(virtualRoot); - _rootUrl = VirtualPathUtility.ToAbsolute(virtualRoot); + if (virtualRoot == null) throw new ArgumentNullException("virtualRoot"); + if (!virtualRoot.StartsWith("~/")) + throw new ArgumentException("The virtualRoot argument must be a virtual path and start with '~/'"); + + RootPath = IOHelper.MapPath(virtualRoot); + _rootUrl = IOHelper.ResolveUrl(virtualRoot); } public PhysicalFileSystem(string rootPath, string rootUrl) @@ -30,7 +33,10 @@ namespace Umbraco.Core.IO if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentException("The argument 'rootUrl' cannot be null or empty."); - _rootPath = rootPath; + if (rootPath.StartsWith("~/")) + throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'"); + + RootPath = rootPath; _rootUrl = rootUrl; } @@ -141,7 +147,7 @@ namespace Umbraco.Core.IO } catch (FileNotFoundException ex) { - LogHelper.Info(string.Format("DeleteFile failed with FileNotFoundException: {0}", ex.InnerException)); + LogHelper.Info(string.Format("DeleteFile failed with FileNotFoundException: {0}", ex.InnerException)); } } @@ -150,17 +156,12 @@ namespace Umbraco.Core.IO return File.Exists(GetFullPath(path)); } - public string GetExtension(string path) - { - return Path.GetExtension(GetFullPath(path)); - } - public string GetRelativePath(string fullPathOrUrl) { var relativePath = fullPathOrUrl .TrimStart(_rootUrl) .Replace('/', Path.DirectorySeparatorChar) - .TrimStart(_rootPath) + .TrimStart(RootPath) .TrimStart(Path.DirectorySeparatorChar); return relativePath; @@ -168,8 +169,8 @@ namespace Umbraco.Core.IO public string GetFullPath(string path) { - return !path.StartsWith(_rootPath) - ? Path.Combine(_rootPath, path) + return !path.StartsWith(RootPath) + ? Path.Combine(RootPath, path) : path; } diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index eca2b79984..61cb495679 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -19,25 +19,28 @@ namespace Umbraco.Core.Models private DateTime? _releaseDate; private DateTime? _expireDate; private int _writer; - private string _nodeName; + private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name. /// /// Constructor for creating a Content object /// - /// Id of the Parent content + /// Name of the content + /// Parent object /// ContentType for the current Content object - public Content(int parentId, IContentType contentType) - : this(parentId, contentType, new PropertyCollection()) - { - } - - public Content(IContent parent, IContentType contentType) - : this(parent, contentType, new PropertyCollection()) + public Content(string name, IContent parent, IContentType contentType) + : this(name, parent, contentType, new PropertyCollection()) { } - public Content(IContent parent, IContentType contentType, PropertyCollection properties) - : base(parent, contentType, properties) + /// + /// Constructor for creating a Content object + /// + /// Name of the content + /// Parent object + /// ContentType for the current Content object + /// Collection of properties + public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties) + : base(name, parent, contentType, properties) { Mandate.ParameterNotNull(contentType, "contentType"); @@ -47,11 +50,23 @@ namespace Umbraco.Core.Models /// /// Constructor for creating a Content object /// + /// Name of the content + /// Id of the Parent content + /// ContentType for the current Content object + public Content(string name, int parentId, IContentType contentType) + : this(name, parentId, contentType, new PropertyCollection()) + { + } + + /// + /// Constructor for creating a Content object + /// + /// Name of the content /// Id of the Parent content /// ContentType for the current Content object /// Collection of properties - public Content(int parentId, IContentType contentType, PropertyCollection properties) - : base(parentId, contentType, properties) + public Content(string name, int parentId, IContentType contentType, PropertyCollection properties) + : base(name, parentId, contentType, properties) { Mandate.ParameterNotNull(contentType, "contentType"); @@ -189,6 +204,12 @@ namespace Umbraco.Core.Models } } + /// + /// Name of the Node (non-localized). + /// + /// + /// This Property is kept internal until localization is introduced. + /// internal string NodeName { get { return _nodeName; } @@ -246,20 +267,20 @@ namespace Umbraco.Core.Models /// /// Changes the Published state of the content object /// - /// Boolean indicating whether content is published (true) or unpublished (false) - public void ChangePublishedState(bool isPublished) + public void ChangePublishedState(PublishedState state) { - Published = isPublished; - //NOTE Should this be checked against the Expire/Release dates? - //TODO possibly create new (unpublished version)? + Published = state == PublishedState.Published; + PublishedState = state; } + internal PublishedState PublishedState { get; set; } + /// /// Changes the Trashed state of the content object /// /// Boolean indicating whether content is trashed (true) or not trashed (false) /// - public void ChangeTrashedState(bool isTrashed, int parentId = -1) + public override void ChangeTrashedState(bool isTrashed, int parentId = -1) { Trashed = isTrashed; @@ -276,7 +297,7 @@ namespace Umbraco.Core.Models //If the content is trashed and is published it should be marked as unpublished if (isTrashed && Published) { - ChangePublishedState(false); + ChangePublishedState(PublishedState.Unpublished); } } diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 8ffc4dc101..abac49248f 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using System.Web; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models @@ -16,7 +17,7 @@ namespace Umbraco.Core.Models { protected IContentTypeComposition ContentTypeBase; private Lazy _parentId; - private string _name; + private string _name;//NOTE Once localization is introduced this will be the localized Name of the Content/Media. private int _sortOrder; private int _level; private string _path; @@ -25,34 +26,50 @@ namespace Umbraco.Core.Models private int _contentTypeId; private PropertyCollection _properties; - protected ContentBase(int parentId, IContentTypeComposition contentType, PropertyCollection properties) + /// + /// Protected constructor for ContentBase (Base for Content and Media) + /// + /// Localized Name of the entity + /// + /// + /// + protected ContentBase(string name, int parentId, IContentTypeComposition contentType, PropertyCollection properties) { Mandate.ParameterCondition(parentId != 0, "parentId"); Mandate.ParameterNotNull(contentType, "contentType"); Mandate.ParameterNotNull(properties, "properties"); - _parentId = new Lazy(() => parentId); - - _contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture)); ContentTypeBase = contentType; + Version = Guid.NewGuid(); + + _parentId = new Lazy(() => parentId); + _name = name; + _contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture)); _properties = properties; _properties.EnsurePropertyTypes(PropertyTypes); - Version = Guid.NewGuid(); } - protected ContentBase(IContentBase parent, IContentTypeComposition contentType, PropertyCollection properties) + /// + /// Protected constructor for ContentBase (Base for Content and Media) + /// + /// Localized Name of the entity + /// + /// + /// + protected ContentBase(string name, IContentBase parent, IContentTypeComposition contentType, PropertyCollection properties) { Mandate.ParameterNotNull(parent, "parent"); Mandate.ParameterNotNull(contentType, "contentType"); Mandate.ParameterNotNull(properties, "properties"); - _parentId = new Lazy(() => parent.Id); + ContentTypeBase = contentType; + Version = Guid.NewGuid(); + _parentId = new Lazy(() => parent.Id); + _name = name; _contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture)); - ContentTypeBase = contentType; _properties = properties; _properties.EnsurePropertyTypes(PropertyTypes); - Version = Guid.NewGuid(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -256,11 +273,110 @@ namespace Umbraco.Core.Models } /// - /// Sets the value of a Property + /// Sets the value of a Property /// /// Alias of the PropertyType /// Value to set for the Property public virtual void SetValue(string propertyTypeAlias, object value) + { + if (value == null) + { + SetValueOnProperty(propertyTypeAlias, value); + return; + } + + // .NET magic to call one of the 'SetPropertyValue' handlers with matching signature + ((dynamic)this).SetPropertyValue(propertyTypeAlias, (dynamic)value); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, string value) + { + SetValueOnProperty(propertyTypeAlias, value); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, int value) + { + SetValueOnProperty(propertyTypeAlias, value); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, long value) + { + string val = value.ToString(); + SetValueOnProperty(propertyTypeAlias, val); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, bool value) + { + int val = Convert.ToInt32(value); + SetValueOnProperty(propertyTypeAlias, val); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, DateTime value) + { + SetValueOnProperty(propertyTypeAlias, value); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, HttpPostedFile value) + { + ContentExtensions.SetValue(this, propertyTypeAlias, value); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, HttpPostedFileBase value) + { + ContentExtensions.SetValue(this, propertyTypeAlias, value); + } + + /// + /// Sets the value of a Property + /// + /// Alias of the PropertyType + /// Value to set for the Property + public virtual void SetPropertyValue(string propertyTypeAlias, HttpPostedFileWrapper value) + { + ContentExtensions.SetValue(this, propertyTypeAlias, value); + } + + /// + /// Private method to set the value of a property + /// + /// + /// + private void SetValueOnProperty(string propertyTypeAlias, object value) { if (Properties.Contains(propertyTypeAlias)) { @@ -284,5 +400,7 @@ namespace Umbraco.Core.Models { return Properties.Any(property => !property.IsValid()) == false; } + + public abstract void ChangeTrashedState(bool isTrashed, int parentId = -1); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index df4696ad30..ae8cd774ed 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -2,6 +2,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; +using System.Globalization; using System.IO; using System.Linq; using System.Web; @@ -53,13 +54,14 @@ 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) + public static void SetPropertyValue(this IMedia media, string propertyTypeAlias, HttpPostedFileBase value) { var name = IOHelper.SafeFileName( @@ -77,7 +79,7 @@ namespace Umbraco.Core.Models /// 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) + public static void SetPropertyValue(this IMedia media, string propertyTypeAlias, HttpPostedFile value) { var name = IOHelper.SafeFileName( @@ -95,19 +97,19 @@ namespace Umbraco.Core.Models /// 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) + public static void SetPropertyValue(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) + public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value) { var name = IOHelper.SafeFileName( @@ -125,7 +127,7 @@ namespace Umbraco.Core.Models /// 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) + public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFile value) { var name = IOHelper.SafeFileName( @@ -143,7 +145,7 @@ namespace Umbraco.Core.Models /// 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) + public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileWrapper value) { if (string.IsNullOrEmpty(value.FileName) == false) SetFileOnContent(content, propertyTypeAlias, value.FileName, value.InputStream); @@ -174,10 +176,10 @@ namespace Umbraco.Core.Models //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) + if (property.PropertyType.DataTypeId == uploadFieldId) { //Get Prevalues by the DataType's Id: property.PropertyType.DataTypeId - var values = ApplicationContext.Current.Services.DataTypeService.GetPreValuesByDataTypeId(property.PropertyType.DataTypeId); + var values = ApplicationContext.Current.Services.DataTypeService.GetPreValuesByDataTypeId(property.PropertyType.DataTypeDefinitionId); var thumbnailSizes = values.FirstOrDefault(); //Additional thumbnails configured as prevalues on the DataType if (thumbnailSizes != null) @@ -208,8 +210,8 @@ namespace Umbraco.Core.Models //Only add dimensions to web images if (supportsResizing) { - SetPropertyValue(content, uploadFieldConfigNode, "widthFieldAlias", GetDimensions(fs, fileName).Item1); - SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", GetDimensions(fs, fileName).Item2); + SetPropertyValue(content, uploadFieldConfigNode, "widthFieldAlias", GetDimensions(fs, fileName).Item1.ToString(CultureInfo.InvariantCulture)); + SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", GetDimensions(fs, fileName).Item2.ToString(CultureInfo.InvariantCulture)); } else { @@ -217,7 +219,7 @@ namespace Umbraco.Core.Models SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", string.Empty); } - SetPropertyValue(content, uploadFieldConfigNode, "lengthFieldAlias", fs.GetSize(fileName)); + SetPropertyValue(content, uploadFieldConfigNode, "lengthFieldAlias", fs.GetSize(fileName).ToString(CultureInfo.InvariantCulture)); SetPropertyValue(content, uploadFieldConfigNode, "extensionFieldAlias", extension); } } @@ -226,7 +228,7 @@ namespace Umbraco.Core.Models property.Value = fs.GetUrl(fileName); } - private static void SetPropertyValue(IContentBase content, XmlNode uploadFieldConfigNode, string propertyAlias, object propertyValue) + private static void SetPropertyValue(IContentBase content, XmlNode uploadFieldConfigNode, string propertyAlias, string propertyValue) { XmlNode propertyNode = uploadFieldConfigNode.SelectSingleNode(propertyAlias); if (propertyNode != null && string.IsNullOrEmpty(propertyNode.FirstChild.Value) == false) @@ -403,9 +405,7 @@ namespace Umbraco.Core.Models { //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); - //var nodeName = content.ContentType.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); - var nodeName = content.ContentType.Alias; - + var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : content.ContentType.Alias.ToSafeAliasWithForcingCheck(); var x = content.ToXml(nodeName); x.Add(new XAttribute("nodeType", content.ContentType.Id)); x.Add(new XAttribute("creatorName", content.GetCreatorProfile().Name)); @@ -458,7 +458,8 @@ namespace Umbraco.Core.Models new XAttribute("nodeName", contentBase.Name), new XAttribute("urlName", niceUrl),//Format Url ? new XAttribute("path", contentBase.Path), - new XAttribute("isDoc", "")); + new XAttribute("isDoc", ""), + UmbracoSettings.UseLegacyXmlSchema ? new XAttribute("nodeTypeAlias", content.ContentType.Alias) : null); foreach (var property in contentBase.Properties) { diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index 9d747e4c55..9211e1ac98 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -72,6 +72,12 @@ namespace Umbraco.Core.Models /// Default public void SetDefaultTemplate(ITemplate template) { + if (template == null) + { + DefaultTemplateId = 0; + return; + } + DefaultTemplateId = template.Id; if(_allowedTemplates.Any(x => x != null && x.Id == template.Id) == false) { diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 5d86e4cc2c..f0dad5f280 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized; using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using System.Text.RegularExpressions; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models @@ -29,6 +30,7 @@ namespace Umbraco.Core.Models private bool _isContainer; private bool _trashed; private PropertyGroupCollection _propertyGroups; + private PropertyTypeCollection _propertyTypes; private IEnumerable _allowedContentTypes; protected ContentTypeBase(int parentId) @@ -38,6 +40,7 @@ namespace Umbraco.Core.Models _parentId = new Lazy(() => parentId); _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); + _propertyTypes = new PropertyTypeCollection(); } protected ContentTypeBase(IContentTypeBase parent) @@ -47,6 +50,7 @@ namespace Umbraco.Core.Models _parentId = new Lazy(() => parent.Id); _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); + _propertyTypes = new PropertyTypeCollection(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -64,12 +68,18 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); private static readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedContentTypes); private static readonly PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); + private static readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo>(x => x.PropertyTypes); protected void PropertyGroupsChanged(object sender, NotifyCollectionChangedEventArgs e) { OnPropertyChanged(PropertyGroupCollectionSelector); } + protected void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + OnPropertyChanged(PropertyTypeCollectionSelector); + } + /// /// Gets or sets the Id of the Parent entity /// @@ -144,7 +154,12 @@ namespace Umbraco.Core.Models get { return _alias; } set { - _alias = value; + //Ensures a valid ContentType alias + //Would have liked to use .ToUmbracoAlias() but that would break casing upon saving older/upgraded ContentTypes + var result = Regex.Replace(value, @"[^a-zA-Z0-9\s\.-]+", "", RegexOptions.Compiled); + result = result.Replace(" ", ""); + + _alias = result; OnPropertyChanged(AliasSelector); } } @@ -301,7 +316,16 @@ namespace Umbraco.Core.Models [IgnoreDataMember] public virtual IEnumerable PropertyTypes { - get { return PropertyGroups.SelectMany(x => x.PropertyTypes); } + get + { + var types = _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes)); + return types; + } + internal set + { + _propertyTypes = new PropertyTypeCollection(value); + _propertyTypes.CollectionChanged += PropertyTypesChanged; + } } /// diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 6583184d76..230b6693e5 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -69,14 +69,6 @@ namespace Umbraco.Core.Models /// /// Changes the Published state of the content object /// - /// Boolean indicating whether content is published (true) or unpublished (false) - void ChangePublishedState(bool isPublished); - - /// - /// Changes the Trashed state of the content object - /// - /// Boolean indicating whether content is trashed (true) or not trashed (false) - /// - void ChangeTrashedState(bool isTrashed, int parentId = -1); + void ChangePublishedState(PublishedState state); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index 7f7ae49c96..42174ee757 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -66,7 +66,7 @@ namespace Umbraco.Core.Models TPassType GetValue(string propertyTypeAlias); /// - /// Sets the value of a Property + /// Sets the value of a Property /// /// Alias of the PropertyType /// Value to set for the Property @@ -77,5 +77,12 @@ namespace Umbraco.Core.Models /// /// True if content is valid otherwise false bool IsValid(); + + /// + /// Changes the Trashed state of the content object + /// + /// Boolean indicating whether content is trashed (true) or not trashed (false) + /// + void ChangeTrashedState(bool isTrashed, int parentId = -1); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Media.cs b/src/Umbraco.Core/Models/Media.cs index aed1cb8b47..8c54c9e2dc 100644 --- a/src/Umbraco.Core/Models/Media.cs +++ b/src/Umbraco.Core/Models/Media.cs @@ -15,20 +15,23 @@ namespace Umbraco.Core.Models /// /// Constructor for creating a Media object /// - /// + /// ame of the Media object + /// Parent object /// MediaType for the current Media object - public Media(int parentId, IMediaType contentType) - : this(parentId, contentType, new PropertyCollection()) - { - } - - public Media(IMedia parent, IMediaType contentType) - : this(parent, contentType, new PropertyCollection()) + public Media(string name, IMedia parent, IMediaType contentType) + : this(name, parent, contentType, new PropertyCollection()) { } - public Media(IMedia parent, IMediaType contentType, PropertyCollection properties) - : base(parent, contentType, properties) + /// + /// Constructor for creating a Media object + /// + /// ame of the Media object + /// Parent object + /// MediaType for the current Media object + /// Collection of properties + public Media(string name, IMedia parent, IMediaType contentType, PropertyCollection properties) + : base(name, parent, contentType, properties) { Mandate.ParameterNotNull(contentType, "contentType"); _contentType = contentType; @@ -37,10 +40,23 @@ namespace Umbraco.Core.Models /// /// Constructor for creating a Media object /// - /// + /// ame of the Media object + /// Id of the Parent IMedia + /// MediaType for the current Media object + public Media(string name, int parentId, IMediaType contentType) + : this(name, parentId, contentType, new PropertyCollection()) + { + } + + /// + /// Constructor for creating a Media object + /// + /// Name of the Media object + /// Id of the Parent IMedia /// MediaType for the current Media object /// Collection of properties - public Media(int parentId, IMediaType contentType, PropertyCollection properties) : base(parentId, contentType, properties) + public Media(string name, int parentId, IMediaType contentType, PropertyCollection properties) + : base(name, parentId, contentType, properties) { Mandate.ParameterNotNull(contentType, "contentType"); _contentType = contentType; @@ -95,7 +111,7 @@ namespace Umbraco.Core.Models /// /// Boolean indicating whether content is trashed (true) or not trashed (false) /// - internal void ChangeTrashedState(bool isTrashed, int parentId = -1) + public override void ChangeTrashedState(bool isTrashed, int parentId = -1) { Trashed = isTrashed; diff --git a/src/Umbraco.Core/Models/PropertyExtensions.cs b/src/Umbraco.Core/Models/PropertyExtensions.cs index 0f3a40fe07..c4568e8472 100644 --- a/src/Umbraco.Core/Models/PropertyExtensions.cs +++ b/src/Umbraco.Core/Models/PropertyExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Xml; using System.Xml.Linq; +using Umbraco.Core.Configuration; namespace Umbraco.Core.Models { @@ -13,7 +14,7 @@ namespace Umbraco.Core.Models /// Xml of the property and its value public static XElement ToXml(this Property property) { - string nodeName = property.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); + string nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias(); var xd = new XmlDocument(); XmlNode xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, ""); diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 3e3fdcc72e..c67a2bd2ed 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -17,8 +17,9 @@ namespace Umbraco.Core.Models private string _name; private string _alias; private string _description; - private int _dataTypeId; - private Guid _dataTypeControlId; + private int _dataTypeDefinitionId; + private int _propertyGroupId; + private Guid _dataTypeId; private DataTypeDatabaseType _dataTypeDatabaseType; private bool _mandatory; private string _helpText; @@ -28,9 +29,9 @@ namespace Umbraco.Core.Models public PropertyType(IDataTypeDefinition dataTypeDefinition) { if(dataTypeDefinition.HasIdentity) - DataTypeId = dataTypeDefinition.Id; + DataTypeDefinitionId = dataTypeDefinition.Id; - DataTypeControlId = dataTypeDefinition.ControlId; + DataTypeId = dataTypeDefinition.ControlId; DataTypeDatabaseType = dataTypeDefinition.DatabaseType; EnsureSerializationService(); @@ -38,7 +39,7 @@ namespace Umbraco.Core.Models internal PropertyType(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType) { - DataTypeControlId = dataTypeControlId; + DataTypeId = dataTypeControlId; DataTypeDatabaseType = dataTypeDatabaseType; EnsureSerializationService(); @@ -53,13 +54,14 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); private static readonly PropertyInfo DescriptionSelector = ExpressionHelper.GetPropertyInfo(x => x.Description); - private static readonly PropertyInfo DataTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.DataTypeId); - private static readonly PropertyInfo DataTypeControlIdSelector = ExpressionHelper.GetPropertyInfo(x => x.DataTypeControlId); + private static readonly PropertyInfo DataTypeDefinitionIdSelector = ExpressionHelper.GetPropertyInfo(x => x.DataTypeDefinitionId); + private static readonly PropertyInfo DataTypeControlIdSelector = ExpressionHelper.GetPropertyInfo(x => x.DataTypeId); private static readonly PropertyInfo DataTypeDatabaseTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.DataTypeDatabaseType); private static readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo(x => x.Mandatory); private static readonly PropertyInfo HelpTextSelector = ExpressionHelper.GetPropertyInfo(x => x.HelpText); private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); private static readonly PropertyInfo ValidationRegExpSelector = ExpressionHelper.GetPropertyInfo(x => x.ValidationRegExp); + private static readonly PropertyInfo PropertyGroupIdSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroupId); /// /// Gets of Sets the Name of the PropertyType @@ -108,13 +110,13 @@ namespace Umbraco.Core.Models /// /// This is actually the Id of the [DataMember] - public int DataTypeId + public int DataTypeDefinitionId { - get { return _dataTypeId; } + get { return _dataTypeDefinitionId; } set { - _dataTypeId = value; - OnPropertyChanged(DataTypeIdSelector); + _dataTypeDefinitionId = value; + OnPropertyChanged(DataTypeDefinitionIdSelector); } } @@ -123,12 +125,12 @@ namespace Umbraco.Core.Models /// /// This is the Id of the actual DataType control [DataMember] - internal Guid DataTypeControlId + public Guid DataTypeId { - get { return _dataTypeControlId; } - set + get { return _dataTypeId; } + internal set { - _dataTypeControlId = value; + _dataTypeId = value; OnPropertyChanged(DataTypeControlIdSelector); } } @@ -147,6 +149,20 @@ namespace Umbraco.Core.Models } } + /// + /// Gets or Sets the PropertyGroup's Id for which this PropertyType belongs + /// + [DataMember] + internal int PropertyGroupId + { + get { return _propertyGroupId; } + set + { + _propertyGroupId = value; + OnPropertyChanged(PropertyGroupIdSelector); + } + } + /// /// Gets of Sets the Boolean indicating whether a value for this PropertyType is required /// @@ -280,7 +296,7 @@ namespace Umbraco.Core.Models return argument == type; }*/ - if (DataTypeControlId != Guid.Empty) + if (DataTypeId != Guid.Empty) { //Find DataType by Id //IDataType dataType = DataTypesResolver.Current.GetById(DataTypeControlId); diff --git a/src/Umbraco.Core/Models/PropertyTypeExtensions.cs b/src/Umbraco.Core/Models/PropertyTypeExtensions.cs index 9347760b41..8a4d35c3af 100644 --- a/src/Umbraco.Core/Models/PropertyTypeExtensions.cs +++ b/src/Umbraco.Core/Models/PropertyTypeExtensions.cs @@ -19,8 +19,8 @@ namespace Umbraco.Core.Models internal static IDataType DataType(this PropertyType propertyType, int propertyId) { Mandate.ParameterNotNull(propertyType, "propertyType"); - var dataType = ApplicationContext.Current.Services.DataTypeService.GetDataTypeById(propertyType.DataTypeControlId); - dataType.DataTypeDefinitionId = propertyType.DataTypeId; + var dataType = ApplicationContext.Current.Services.DataTypeService.GetDataTypeById(propertyType.DataTypeId); + dataType.DataTypeDefinitionId = propertyType.DataTypeDefinitionId; dataType.Data.PropertyId = propertyId; return dataType; } diff --git a/src/Umbraco.Core/Models/PublishedState.cs b/src/Umbraco.Core/Models/PublishedState.cs new file mode 100644 index 0000000000..4469eba9cc --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedState.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Models +{ + public enum PublishedState + { + Published, + Unpublished, + Saved + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/ContentTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentTypeDto.cs index 49c8db119e..8d163abf44 100644 --- a/src/Umbraco.Core/Models/Rdbms/ContentTypeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentTypeDto.cs @@ -43,11 +43,6 @@ namespace Umbraco.Core.Models.Rdbms [Constraint(Default = "0")] public bool AllowAtRoot { get; set; } - [Column("masterContentType")] - [Constraint(Default = "0")] - [NullSetting(NullSetting = NullSettings.Null)] - public int MasterContentType { get; set; }//TODO Delete once "masterContentType" has been removed from the Core - [ResultColumn] public NodeDto NodeDto { get; set; } } diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs index 7fbf15f00f..e3fdfb9810 100644 --- a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Models.Rdbms public int ContentTypeNodeId { get; set; } [Column("templateNodeId")] - /*[ForeignKey(typeof(TemplateDto))]*/ + [ForeignKey(typeof(TemplateDto), Column = "nodeId")] public int TemplateNodeId { get; set; } [Column("IsDefault")] diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs index fa5d5e421e..bb2b80a766 100644 --- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.Caching; +using System.Threading; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Persistence.Caching @@ -23,9 +24,10 @@ namespace Umbraco.Core.Persistence.Caching #endregion - private readonly ObjectCache _memoryCache = new MemoryCache("in-memory"); //TODO Save this in cache as well, so its not limited to a single server usage private ConcurrentDictionary _keyTracker = new ConcurrentDictionary(); + private ObjectCache _memoryCache = new MemoryCache("in-memory"); + private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim(); public IEntity GetById(Type type, Guid id) { @@ -77,6 +79,16 @@ namespace Umbraco.Core.Persistence.Caching _keyTracker.TryRemove(key, out throwaway); } + public void Clear() + { + using (new ReadLock(ClearLock)) + { + _keyTracker.Clear(); + _memoryCache.DisposeIfDisposable(); + _memoryCache = new MemoryCache("in-memory"); + } + } + private string GetCompositeId(Type type, Guid id) { return string.Format("{0}-{1}", type.Name, id.ToString()); diff --git a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs index fe4574b4b0..150cb25088 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Factories public IContent BuildEntity(DocumentDto dto) { - return new Content(dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType) + return new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType) { Id = _id, Key = @@ -57,20 +57,23 @@ namespace Umbraco.Core.Persistence.Factories public DocumentDto BuildDto(IContent entity) { - //NOTE Currently doesn't add Alias and templateId (legacy stuff that eventually will go away) + //NOTE Currently doesn't add Alias (legacy that eventually will go away) var documentDto = new DocumentDto - { - Newest = true, - NodeId = entity.Id, - Published = entity.Published, - Text = entity.Name, - UpdateDate = entity.UpdateDate, - WriterUserId = entity.WriterId, - VersionId = entity.Version, - ExpiresDate = null, - ReleaseDate = null, - ContentVersionDto = BuildContentVersionDto(entity) - }; + { + Newest = true, + NodeId = entity.Id, + Published = entity.Published, + Text = entity.Name, + UpdateDate = entity.UpdateDate, + WriterUserId = entity.WriterId, + VersionId = entity.Version, + ExpiresDate = null, + ReleaseDate = null, + ContentVersionDto = BuildContentVersionDto(entity) + }; + + if (entity.Template != null && entity.Template.Id > 0) + documentDto.TemplateId = entity.Template.Id; if (entity.ExpireDate.HasValue) documentDto.ExpiresDate = entity.ExpireDate.Value; diff --git a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs index 16fe38492b..f4d04b1373 100644 --- a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs @@ -29,14 +29,13 @@ namespace Umbraco.Core.Persistence.Factories public IMedia BuildEntity(ContentVersionDto dto) { - return new Models.Media(dto.ContentDto.NodeDto.ParentId, _contentType) + return new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, _contentType) { Id = _id, Key = dto.ContentDto.NodeDto.UniqueId.HasValue ? dto.ContentDto.NodeDto.UniqueId.Value : _id.ToGuid(), - Name = dto.ContentDto.NodeDto.Text, Path = dto.ContentDto.NodeDto.Path, CreatorId = dto.ContentDto.NodeDto.UserId.Value, Level = dto.ContentDto.NodeDto.Level, diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 2dd018ac55..886b9007da 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -32,13 +32,19 @@ namespace Umbraco.Core.Persistence.Factories public IEnumerable BuildEntity(IEnumerable dtos) { var properties = new List(); + foreach (var dto in dtos) { - var propertyType = _contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Id == dto.PropertyTypeId); - var property = propertyType.CreatePropertyFromRawValue(dto.GetValue, dto.VersionId.Value, dto.Id); - property.ResetDirtyProperties(); - properties.Add(property); + if (_contentType.CompositionPropertyTypes.Any(x => x.Id == dto.PropertyTypeId)) + { + var propertyType = _contentType.CompositionPropertyTypes.First(x => x.Id == dto.PropertyTypeId); + var property = propertyType.CreatePropertyFromRawValue(dto.GetValue, dto.VersionId.Value, dto.Id); + + property.ResetDirtyProperties(); + properties.Add(property); + } } + return properties; } @@ -95,10 +101,14 @@ namespace Umbraco.Core.Persistence.Factories var properties = new List(); foreach (var dto in dtos) { - var propertyType = _mediaType.PropertyTypes.FirstOrDefault(x => x.Id == dto.PropertyTypeId); - var property = propertyType.CreatePropertyFromRawValue(dto.GetValue, dto.VersionId.Value, dto.Id); - property.ResetDirtyProperties(); - properties.Add(property); + if (_mediaType.CompositionPropertyTypes.Any(x => x.Id == dto.PropertyTypeId)) + { + var propertyType = _mediaType.CompositionPropertyTypes.First(x => x.Id == dto.PropertyTypeId); + var property = propertyType.CreatePropertyFromRawValue(dto.GetValue, dto.VersionId.Value, dto.Id); + + property.ResetDirtyProperties(); + properties.Add(property); + } } return properties; } diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs index 712721af4f..fb25e3fe32 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs @@ -28,19 +28,22 @@ namespace Umbraco.Core.Persistence.Factories group.SortOrder = groupDto.SortOrder; group.PropertyTypes = new PropertyTypeCollection(); - foreach (var typeDto in groupDto.PropertyTypeDtos) + //Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded + var typeDtos = groupDto.PropertyTypeDtos.Where(x => x.Id > 0); + foreach (var typeDto in typeDtos) { group.PropertyTypes.Add(new PropertyType(typeDto.DataTypeDto.ControlId, typeDto.DataTypeDto.DbType.EnumParse(true)) { Alias = typeDto.Alias, - DataTypeId = typeDto.DataTypeId, + DataTypeDefinitionId = typeDto.DataTypeId, Description = typeDto.Description, Id = typeDto.Id, Name = typeDto.Name, HelpText = typeDto.HelpText, Mandatory = typeDto.Mandatory, - SortOrder = typeDto.SortOrder + SortOrder = typeDto.SortOrder, + PropertyGroupId = groupDto.Id }); } @@ -53,7 +56,7 @@ namespace Umbraco.Core.Persistence.Factories public IEnumerable BuildDto(IEnumerable entity) { - return entity.Select(propertyGroup => BuildGroupDto(propertyGroup)).ToList(); + return entity.Select(BuildGroupDto).ToList(); } #endregion @@ -82,15 +85,17 @@ namespace Umbraco.Core.Persistence.Factories { Alias = propertyType.Alias, ContentTypeId = _id, - DataTypeId = propertyType.DataTypeId, + DataTypeId = propertyType.DataTypeDefinitionId, Description = propertyType.Description, HelpText = propertyType.HelpText, Mandatory = propertyType.Mandatory, Name = propertyType.Name, - SortOrder = propertyType.SortOrder, - PropertyTypeGroupId = tabId + SortOrder = propertyType.SortOrder }; + if (tabId != default(int)) + propertyTypeDto.PropertyTypeGroupId = tabId; + if (propertyType.HasIdentity) propertyTypeDto.Id = propertyType.Id; diff --git a/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs index df3967ad85..1df4c94ee1 100644 --- a/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs @@ -29,14 +29,14 @@ namespace Umbraco.Core.Persistence.Mappers { CacheMap(src => src.Id, dto => dto.Id); CacheMap(src => src.Alias, dto => dto.Alias); - CacheMap(src => src.DataTypeId, dto => dto.DataTypeId); + CacheMap(src => src.DataTypeDefinitionId, dto => dto.DataTypeId); CacheMap(src => src.Description, dto => dto.Description); CacheMap(src => src.HelpText, dto => dto.HelpText); CacheMap(src => src.Mandatory, dto => dto.Mandatory); CacheMap(src => src.Name, dto => dto.Name); CacheMap(src => src.SortOrder, dto => dto.SortOrder); CacheMap(src => src.ValidationRegExp, dto => dto.ValidationRegExp); - CacheMap(src => src.DataTypeControlId, dto => dto.ControlId); + CacheMap(src => src.DataTypeId, dto => dto.ControlId); CacheMap(src => src.DataTypeDatabaseType, dto => dto.DbType); } } diff --git a/src/Umbraco.Core/Persistence/Migrations/IMigrationContext.cs b/src/Umbraco.Core/Persistence/Migrations/IMigrationContext.cs index 616c7b16c5..159efb6ff8 100644 --- a/src/Umbraco.Core/Persistence/Migrations/IMigrationContext.cs +++ b/src/Umbraco.Core/Persistence/Migrations/IMigrationContext.cs @@ -6,5 +6,6 @@ namespace Umbraco.Core.Persistence.Migrations { ICollection Expressions { get; set; } DatabaseProviders CurrentDatabaseProvider { get; } + Database Database { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs index 725cb2c7a0..642a920319 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs @@ -11,61 +11,61 @@ namespace Umbraco.Core.Persistence.Migrations { public abstract class MigrationBase : IMigration { - internal IMigrationContext _context; + internal IMigrationContext Context; public abstract void Up(); public abstract void Down(); public virtual void GetUpExpressions(IMigrationContext context) { - _context = context; + Context = context; Up(); } public virtual void GetDownExpressions(IMigrationContext context) { - _context = context; + Context = context; Down(); } public IAlterSyntaxBuilder Alter { - get { return new AlterSyntaxBuilder(_context); } + get { return new AlterSyntaxBuilder(Context); } } public ICreateBuilder Create { - get { return new CreateBuilder(_context); } + get { return new CreateBuilder(Context); } } public IDeleteBuilder Delete { - get { return new DeleteBuilder(_context); } + get { return new DeleteBuilder(Context); } } public IExecuteBuilder Execute { - get { return new ExecuteBuilder(_context); } + get { return new ExecuteBuilder(Context); } } public IInsertBuilder Insert { - get { return new InsertBuilder(_context); } + get { return new InsertBuilder(Context); } } public IRenameBuilder Rename { - get { return new RenameBuilder(_context); } + get { return new RenameBuilder(Context); } } public IUpdateBuilder Update { - get { return new UpdateBuilder(_context); } + get { return new UpdateBuilder(Context); } } public IIfDatabaseBuilder IfDatabase(params DatabaseProviders[] databaseProviders) { - return new IfDatabaseBuilder(_context, databaseProviders); + return new IfDatabaseBuilder(Context, databaseProviders); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs index 90779e4317..e173bccc6d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs @@ -5,14 +5,17 @@ namespace Umbraco.Core.Persistence.Migrations { internal class MigrationContext : IMigrationContext { - public MigrationContext(DatabaseProviders databaseProvider) + public MigrationContext(DatabaseProviders databaseProvider, Database database) { Expressions = new Collection(); CurrentDatabaseProvider = databaseProvider; + Database = database; } public ICollection Expressions { get; set; } public DatabaseProviders CurrentDatabaseProvider { get; private set; } + + public Database Database { get; private set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs new file mode 100644 index 0000000000..2c9af738ff --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.ObjectResolution; + +namespace Umbraco.Core.Persistence.Migrations +{ + /// + /// A resolver to return all IMigrations + /// + internal class MigrationResolver : ManyObjectsResolverBase + { + /// + /// Constructor + /// + /// + /// + /// Use transient objects as we don't want these as singletons and take up memory that is not required + /// + public MigrationResolver(IEnumerable migrations) + : base(migrations, ObjectLifetimeScope.Transient) + { + } + + /// + /// Gets the migrations + /// + public IEnumerable Migrations + { + get { return Values; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs index 4a341e4521..74721961dd 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Events; using Umbraco.Core.Logging; namespace Umbraco.Core.Persistence.Migrations { - /// + /// /// Represents the Migration Runner, which is used to apply migrations to /// the umbraco database. /// @@ -44,13 +45,17 @@ namespace Umbraco.Core.Persistence.Migrations { LogHelper.Info("Initializing database migration"); - var foundMigrations = PluginManager.Current.FindMigrations(); + var foundMigrations = MigrationResolver.Current.Migrations; + var migrations = isUpgrade - ? OrderedUpgradeMigrations(foundMigrations) - : OrderedDowngradeMigrations(foundMigrations); + ? OrderedUpgradeMigrations(foundMigrations).ToList() + : OrderedDowngradeMigrations(foundMigrations).ToList(); + + if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _configuredVersion, _targetVersion, true), this)) + return false; //Loop through migrations to generate sql - var context = new MigrationContext(databaseProvider); + var context = new MigrationContext(databaseProvider, database); foreach (MigrationBase migration in migrations) { if (isUpgrade) @@ -84,6 +89,8 @@ namespace Umbraco.Core.Persistence.Migrations transaction.Complete(); } + Migrated.RaiseEvent(new MigrationEventArgs(migrations, context, _configuredVersion, _targetVersion, false), this); + return true; } @@ -114,5 +121,15 @@ namespace Umbraco.Core.Persistence.Migrations select migration); return migrations; } + + /// + /// Occurs before Migration + /// + public static event TypedEventHandler Migrating; + + /// + /// Occurs after Migration + /// + public static event TypedEventHandler Migrated; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/PluginManagerExtension.cs b/src/Umbraco.Core/Persistence/Migrations/PluginManagerExtension.cs deleted file mode 100644 index 8540f8dbf7..0000000000 --- a/src/Umbraco.Core/Persistence/Migrations/PluginManagerExtension.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Persistence.Migrations -{ - internal static class PluginManagerExtension - { - public static IEnumerable FindMigrations(this PluginManager resolver) - { - var types = resolver.ResolveTypesWithAttribute(); - return resolver.CreateInstances(types); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs index b51646c064..7a70b0991e 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs @@ -5,15 +5,20 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute public class ExecuteBuilder : IExecuteBuilder { private readonly IMigrationContext _context; + private readonly DatabaseProviders[] _databaseProviders; - public ExecuteBuilder(IMigrationContext context) + public ExecuteBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) { _context = context; + _databaseProviders = databaseProviders; } public void Sql(string sqlStatement) { - var expression = new ExecuteSqlStatementExpression {SqlStatement = sqlStatement}; + var expression = _databaseProviders == null + ? new ExecuteSqlStatementExpression {SqlStatement = sqlStatement} + : new ExecuteSqlStatementExpression(_context.CurrentDatabaseProvider, + _databaseProviders) {SqlStatement = sqlStatement}; _context.Expressions.Add(expression); } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs index a7d3c4432e..308a7719dd 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs @@ -14,6 +14,9 @@ public override string ToString() { + if (IsExpressionSupported() == false) + return string.Empty; + return SqlStatement; } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IIfDatabaseBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IIfDatabaseBuilder.cs index 050af489a9..b274c215ea 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IIfDatabaseBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IIfDatabaseBuilder.cs @@ -1,13 +1,17 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Create; using Umbraco.Core.Persistence.Migrations.Syntax.Delete; +using Umbraco.Core.Persistence.Migrations.Syntax.Execute; using Umbraco.Core.Persistence.Migrations.Syntax.Rename; +using Umbraco.Core.Persistence.Migrations.Syntax.Update; namespace Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase { public interface IIfDatabaseBuilder : IFluentSyntax { ICreateBuilder Create { get; } + IExecuteBuilder Execute { get; } IDeleteBuilder Delete { get; } IRenameBuilder Rename { get; } + IUpdateBuilder Update { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs index 6657cb9cf4..b7105170f1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs @@ -1,6 +1,8 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Create; using Umbraco.Core.Persistence.Migrations.Syntax.Delete; +using Umbraco.Core.Persistence.Migrations.Syntax.Execute; using Umbraco.Core.Persistence.Migrations.Syntax.Rename; +using Umbraco.Core.Persistence.Migrations.Syntax.Update; namespace Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase { @@ -20,6 +22,11 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase get { return new CreateBuilder(_context, _databaseProviders); } } + public IExecuteBuilder Execute + { + get { return new ExecuteBuilder(_context, _databaseProviders); } + } + public IDeleteBuilder Delete { get { return new DeleteBuilder(_context, _databaseProviders); } @@ -29,5 +36,10 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase { get { return new RenameBuilder(_context, _databaseProviders); } } + + public IUpdateBuilder Update + { + get { return new UpdateBuilder(_context, _databaseProviders); } + } } } \ 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 c3cfbcda0f..9baa3501cf 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs @@ -22,6 +22,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions public override string ToString() { + if (IsExpressionSupported() == false) + return string.Empty; + var updateItems = new List(); var whereClauses = new List(); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs index 9df3e26f6d..e2e118c05f 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs @@ -5,15 +5,19 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update public class UpdateBuilder : IUpdateBuilder { private readonly IMigrationContext _context; + private readonly DatabaseProviders[] _databaseProviders; - public UpdateBuilder(IMigrationContext context) + public UpdateBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) { _context = context; + _databaseProviders = databaseProviders; } public IUpdateSetSyntax Table(string tableName) { - var expression = new UpdateDataExpression { TableName = tableName }; + var expression = _databaseProviders == null + ? new UpdateDataExpression { TableName = tableName } + : new UpdateDataExpression(_context.CurrentDatabaseProvider, _databaseProviders) { TableName = tableName }; _context.Expressions.Add(expression); return new UpdateDataBuilder(expression, _context); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourEightZero/RemoveUmbracoAppConstraints.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs similarity index 96% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourEightZero/RemoveUmbracoAppConstraints.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs index 22c656abe6..2d687e958b 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourEightZero/RemoveUmbracoAppConstraints.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs @@ -1,7 +1,7 @@ using System.Data; using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourEightZero +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero { [MigrationAttribute("4.8.0", 0, GlobalSettings.UmbracoMigrationName)] public class RemoveUmbracoAppConstraints : MigrationBase diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/DeleteAppTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs similarity index 77% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/DeleteAppTables.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs index 4b41295d3b..567f641e5c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/DeleteAppTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs @@ -1,8 +1,8 @@ using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 10, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 10, GlobalSettings.UmbracoMigrationName)] public class DeleteAppTables : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/EnsureAppsTreesUpdated.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/EnsureAppsTreesUpdated.cs similarity index 83% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/EnsureAppsTreesUpdated.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/EnsureAppsTreesUpdated.cs index 09f5191f68..2da4abf34e 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/EnsureAppsTreesUpdated.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/EnsureAppsTreesUpdated.cs @@ -1,9 +1,9 @@ using System; using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 9, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 9, GlobalSettings.UmbracoMigrationName)] public class EnsureAppsTreesUpdated : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/MoveMasterContentTypeData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/MoveMasterContentTypeData.cs new file mode 100644 index 0000000000..482c02381f --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/MoveMasterContentTypeData.cs @@ -0,0 +1,29 @@ +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix +{ + [Migration("6.0.0", 5, GlobalSettings.UmbracoMigrationName)] + public class MoveMasterContentTypeData : MigrationBase + { + public override void Up() + { + //Reading entries from the cmsContentType table in order to update the parentID on the umbracoNode table. + //NOTE This is primarily done because of a shortcoming in sql ce, which has really bad support for updates (can't use FROM or subqueries with multiple results). + if (base.Context != null && base.Context.Database != null) + { + var list = base.Context.Database.Fetch("SELECT nodeId, masterContentType FROM cmsContentType WHERE not masterContentType is null AND masterContentType != 0"); + foreach (var item in list) + { + Update.Table("umbracoNode").Set(new { parentID = item.masterContentType }).Where(new { id = item.nodeId }); + } + } + + Execute.Sql( + "INSERT INTO cmsContentType2ContentType (parentContentTypeId, childContentTypeId) SELECT masterContentType, nodeId FROM cmsContentType WHERE not masterContentType is null and masterContentType != 0"); + } + + public override void Down() + { + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/NewCmsContentType2ContentTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/NewCmsContentType2ContentTypeTable.cs similarity index 88% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/NewCmsContentType2ContentTypeTable.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/NewCmsContentType2ContentTypeTable.cs index 3366691e0d..7ff03087c4 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/NewCmsContentType2ContentTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/NewCmsContentType2ContentTypeTable.cs @@ -1,8 +1,8 @@ using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 4, GlobalSettings.UmbracoMigrationName)] public class NewCmsContentType2ContentTypeTable : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RemoveMasterContentTypeColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs similarity index 89% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RemoveMasterContentTypeColumn.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs index 7dc9b1bb19..680b2001a0 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RemoveMasterContentTypeColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs @@ -1,8 +1,8 @@ using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 6, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 6, GlobalSettings.UmbracoMigrationName)] public class RemoveMasterContentTypeColumn : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RenameCmsTabTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameCmsTabTable.cs similarity index 80% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RenameCmsTabTable.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameCmsTabTable.cs index 9aaeb6b1a5..0f8635af09 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RenameCmsTabTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameCmsTabTable.cs @@ -1,8 +1,8 @@ using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 0, GlobalSettings.UmbracoMigrationName)] public class RenameCmsTabTable : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RenameTabIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameTabIdColumn.cs similarity index 95% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RenameTabIdColumn.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameTabIdColumn.cs index 31e8f18a0a..6f3f7edc80 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/RenameTabIdColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameTabIdColumn.cs @@ -1,9 +1,9 @@ using System.Data; using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 7, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 7, GlobalSettings.UmbracoMigrationName)] public class RenameTabIdColumn : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentTypeAllowedContentTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeAllowedContentTypeTable.cs similarity index 83% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentTypeAllowedContentTypeTable.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeAllowedContentTypeTable.cs index 3b5d86ad79..be704466a8 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentTypeAllowedContentTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeAllowedContentTypeTable.cs @@ -1,8 +1,8 @@ using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 3, GlobalSettings.UmbracoMigrationName)] public class UpdateCmsContentTypeAllowedContentTypeTable : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs similarity index 86% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentTypeTable.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs index 35f0e76c36..3e6b4cf05c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs @@ -1,8 +1,8 @@ using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 2, GlobalSettings.UmbracoMigrationName)] public class UpdateCmsContentTypeTable : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentVersionTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentVersionTable.cs similarity index 82% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentVersionTable.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentVersionTable.cs index 55d1234022..1dc8998e68 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsContentVersionTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentVersionTable.cs @@ -1,8 +1,8 @@ using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 8, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 8, GlobalSettings.UmbracoMigrationName)] public class UpdateCmsContentVersionTable : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsPropertyTypeGroupTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs similarity index 88% rename from src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsPropertyTypeGroupTable.cs rename to src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs index 62938eb1d3..2bd03f15bc 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/UpdateCmsPropertyTypeGroupTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs @@ -1,9 +1,9 @@ using System.Data; using Umbraco.Core.Configuration; -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [MigrationAttribute("6.0.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 1, GlobalSettings.UmbracoMigrationName)] public class UpdateCmsPropertyTypeGroupTable : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/MoveMasterContentTypeData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/MoveMasterContentTypeData.cs deleted file mode 100644 index 235f7a0a8a..0000000000 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixth/MoveMasterContentTypeData.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixth -{ - [MigrationAttribute("6.0.0", 5, GlobalSettings.UmbracoMigrationName)] - public class MoveMasterContentTypeData : MigrationBase - { - public override void Up() - { - Execute.Sql( - "INSERT INTO cmsContentType2ContentType (parentContentTypeId, childContentTypeId) SELECT masterContentType, nodeId FROM cmsContentType WHERE not masterContentType is null and masterContentType != 0"); - } - - public override void Down() - { - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index d60525a91c..aa531a7c62 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -424,7 +424,14 @@ namespace Umbraco.Core.Persistence } else if (t == typeof(string)) { - p.Size = Math.Max((item as string).Length + 1, 4000); // Help query plan caching by using common size + // out of memory exception occurs if trying to save more than 4000 characters to SQL Server CE NText column. Set before attempting to set Size, or Size will always max out at 4000 + if ((item as string).Length + 1 > 4000 && p.GetType().Name == "SqlCeParameter") + p.GetType().GetProperty("SqlDbType").SetValue(p, SqlDbType.NText, null); + + p.Size = (item as string).Length + 1; + if(p.Size < 4000) + p.Size = Math.Max((item as string).Length + 1, 4000); // Help query plan caching by using common size + p.Value = item; } else if (t == typeof(AnsiString)) diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs index 3e8d562d07..0ab1b0985d 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs @@ -76,7 +76,7 @@ namespace Umbraco.Core.Persistence db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF;", SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(tableName)))); //Special case for MySql - if (ApplicationContext.Current.DatabaseContext.ProviderName.Contains("MySql")) + if (SyntaxConfig.SqlSyntaxProvider is MySqlSyntaxProvider && tableName.Equals("umbracoUser")) { db.Update("SET id = @IdAfter WHERE id = @IdBefore AND userLogin = @Login", new { IdAfter = 0, IdBefore = 1, Login = "admin" }); } diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs index 649467b1ba..3f0495cfd5 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs @@ -150,21 +150,18 @@ namespace Umbraco.Core.Persistence.Querying protected virtual string VisitMemberAccess(MemberExpression m) { - if (m.Expression != null && - m.Expression.NodeType == ExpressionType.Parameter - && m.Expression.Type == typeof(T)) + if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(T)) { var field = _mapper.Map(m.Member.Name); return field; } - if (m.Expression != null && m.Expression.NodeType != ExpressionType.Constant) + if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) { var field = _mapper.Map(m.Member.Name); return field; } - - + var member = Expression.Convert(m, typeof(object)); var lambda = Expression.Lambda>(member); var getter = lambda.Compile(); @@ -190,9 +187,7 @@ namespace Umbraco.Core.Persistence.Querying var r = new StringBuilder(); foreach (Object e in exprs) { - r.AppendFormat("{0}{1}", - r.Length > 0 ? "," : "", - e); + r.AppendFormat("{0}{1}", r.Length > 0 ? "," : "", e); } return r.ToString(); } diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs index 4524095fde..39b0239844 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs @@ -159,13 +159,12 @@ namespace Umbraco.Core.Persistence.Querying return field; } - if (m.Expression != null && m.Expression.NodeType != ExpressionType.Constant) + if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) { string field = GetFieldName(pd, m.Member.Name); return field; } - var member = Expression.Convert(m, typeof(object)); var lambda = Expression.Lambda>(member); var getter = lambda.Compile(); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index c560a68c02..b284a4550a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -40,11 +40,11 @@ namespace Umbraco.Core.Persistence.Repositories protected override IContent PerformGet(int id) { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); - sql.OrderByDescending(x => x.VersionDate); + var sql = GetBaseQuery(false) + .Where(GetBaseWhereClause(), new { Id = id }) + .OrderByDescending(x => x.VersionDate); - var dto = Database.Query(sql).FirstOrDefault(); + var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return null; @@ -56,7 +56,7 @@ namespace Umbraco.Core.Persistence.Repositories var content = factory.BuildEntity(dto); //Check if template id is set on DocumentDto, and get ITemplate if it is. - if (dto.TemplateId.HasValue) + if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) { content.Template = _templateRepository.Get(dto.TemplateId.Value); } @@ -163,7 +163,7 @@ namespace Umbraco.Core.Persistence.Repositories sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId }); sql.OrderByDescending(x => x.VersionDate); - var dto = Database.Query(sql).FirstOrDefault(); + var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return null; @@ -262,8 +262,9 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IContent entity) { + var publishedState = ((Content) entity).PublishedState; //A new version should only be created if published state (or language) has changed - bool shouldCreateNewVersion = ((ICanBeDirty)entity).IsPropertyDirty("Published") || ((ICanBeDirty)entity).IsPropertyDirty("Language"); + bool shouldCreateNewVersion = (((ICanBeDirty)entity).IsPropertyDirty("Published") && publishedState != PublishedState.Unpublished) || ((ICanBeDirty)entity).IsPropertyDirty("Language"); if (shouldCreateNewVersion) { //Updates Modified date and Version Guid @@ -274,11 +275,14 @@ namespace Umbraco.Core.Persistence.Repositories entity.UpdateDate = DateTime.Now; } - //Look up parent to get and set the correct Path if ParentId has changed + //Look up parent to get and set the correct Path and update SortOrder if ParentId has changed if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) { var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId }); entity.Path = string.Concat(parent.Path, ",", entity.Id); + entity.Level = parent.Level + 1; + var maxSortOrder = Database.ExecuteScalar("SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId", new { ParentId = entity.ParentId }); + entity.SortOrder = maxSortOrder; } var factory = new ContentFactory(NodeObjectTypeId, entity.Id); @@ -299,8 +303,9 @@ namespace Umbraco.Core.Persistence.Repositories Database.Update(newContentDto); } - //If Published state has changed then previous versions should have their publish state reset - if (((ICanBeDirty)entity).IsPropertyDirty("Published") && entity.Published) + //If Published state has changed then previous versions should have their publish state reset. + //If state has been changed to unpublished the previous versions publish state should also be reset. + if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished)) { var publishedDocs = Database.Fetch("WHERE nodeId = @Id AND published = @IsPublished", new { Id = entity.Id, IsPublished = true }); foreach (var doc in publishedDocs) @@ -365,6 +370,8 @@ namespace Umbraco.Core.Persistence.Repositories { foreach (var property in entity.Properties) { + if(keyDictionary.ContainsKey(property.PropertyTypeId) == false) continue; + property.Id = keyDictionary[property.PropertyTypeId]; } } @@ -379,7 +386,7 @@ namespace Umbraco.Core.Persistence.Repositories //Loop through properties to check if the content contains images/files that should be deleted foreach (var property in entity.Properties) { - if (property.PropertyType.DataTypeControlId == uploadFieldId && + if (property.PropertyType.DataTypeId == uploadFieldId && string.IsNullOrEmpty(property.Value.ToString()) == false && fs.FileExists(IOHelper.MapPath(property.Value.ToString()))) { @@ -413,7 +420,7 @@ namespace Umbraco.Core.Persistence.Repositories sql.Where(x => x.Language == language); sql.OrderByDescending(x => x.VersionDate); - var dto = Database.Query(sql).FirstOrDefault(); + var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return null; diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 99acdf91d3..c430830b76 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -40,7 +40,8 @@ namespace Umbraco.Core.Persistence.Repositories .RightJoin() .On(left => left.Id, right => right.PropertyTypeGroupId) .InnerJoin() - .On(left => left.DataTypeId, right => right.DataTypeId); + .On(left => left.DataTypeId, right => right.DataTypeId) + .OrderBy(x => x.PropertyTypeGroupId); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); @@ -130,7 +131,7 @@ namespace Umbraco.Core.Persistence.Repositories //Update the current PropertyType with correct ControlId and DatabaseType var dataTypeDto = Database.FirstOrDefault("WHERE nodeId = @Id", new { Id = propertyTypeDto.DataTypeId }); - propertyType.DataTypeControlId = dataTypeDto.ControlId; + propertyType.DataTypeId = dataTypeDto.ControlId; propertyType.DataTypeDatabaseType = dataTypeDto.DbType.EnumParse(true); } } @@ -168,11 +169,19 @@ namespace Umbraco.Core.Persistence.Repositories if (((ICanBeDirty)entity).IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty())) { //Delete PropertyTypes by excepting entries from db with entries from collections - var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { Id = entity.Id }).Select(x => x.Alias); - var entityPropertyTypes = entity.PropertyTypes.Select(x => x.Alias); - var aliases = dbPropertyTypes.Except(entityPropertyTypes); + var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { Id = entity.Id }); + var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Alias.ToLowerInvariant()); + var entityPropertyTypes = entity.PropertyTypes.Select(x => x.Alias.ToLowerInvariant()); + var aliases = dbPropertyTypeAlias.Except(entityPropertyTypes); foreach (var alias in aliases) { + //Before a PropertyType can be deleted, all Properties based on that PropertyType should be deleted. + var propertyType = dbPropertyTypes.FirstOrDefault(x => x.Alias.ToLowerInvariant() == alias); + if (propertyType != null) + { + Database.Delete("WHERE propertytypeid = @Id", new { Id = propertyType.Id }); + } + Database.Delete("WHERE contentTypeId = @Id AND Alias = @Alias", new { Id = entity.Id, Alias = alias }); } //Delete Tabs/Groups by excepting entries from db with entries from collections @@ -184,7 +193,7 @@ namespace Umbraco.Core.Persistence.Repositories Database.Delete("WHERE contenttypeNodeId = @Id AND text = @Name", new { Id = entity.Id, Name = tabName }); } - //Run through all groups and types to insert or update entries + //Run through all groups to insert or update entries foreach (var propertyGroup in entity.PropertyGroups) { var tabDto = propertyFactory.BuildGroupDto(propertyGroup); @@ -193,19 +202,17 @@ namespace Umbraco.Core.Persistence.Repositories : Convert.ToInt32(Database.Insert(tabDto)); if (!propertyGroup.HasIdentity) propertyGroup.Id = groupPrimaryKey;//Set Id on new PropertyGroup + } - //This should indicate that neither group nor property types has been touched, but this implies a deeper 'Dirty'-lookup - //if(!propertyGroup.IsDirty()) continue; - - foreach (var propertyType in propertyGroup.PropertyTypes) - { - var propertyTypeDto = propertyFactory.BuildPropertyTypeDto(propertyGroup.Id, propertyType); - int typePrimaryKey = propertyType.HasIdentity - ? Database.Update(propertyTypeDto) - : Convert.ToInt32(Database.Insert(propertyTypeDto)); - if (!propertyType.HasIdentity) - propertyType.Id = typePrimaryKey;//Set Id on new PropertyType - } + //Run through all PropertyTypes to insert or update entries + foreach (var propertyType in entity.PropertyTypes) + { + var propertyTypeDto = propertyFactory.BuildPropertyTypeDto(propertyType.PropertyGroupId, propertyType); + int typePrimaryKey = propertyType.HasIdentity + ? Database.Update(propertyTypeDto) + : Convert.ToInt32(Database.Insert(propertyTypeDto)); + if (!propertyType.HasIdentity) + propertyType.Id = typePrimaryKey;//Set Id on new PropertyType } } } @@ -226,11 +233,12 @@ namespace Umbraco.Core.Persistence.Repositories var sql = new Sql(); sql.Select("*") .From() - .RightJoin() + .LeftJoin() .On(left => left.Id, right => right.PropertyTypeGroupId) - .InnerJoin() + .LeftJoin() .On(left => left.DataTypeId, right => right.DataTypeId) - .Where(x => x.ContentTypeId == id); + .Where(x => x.ContentTypeNodeId == id) + .OrderBy(x => x.Id); var dtos = Database.Fetch(new GroupPropertyTypeRelator().Map, sql); @@ -238,5 +246,36 @@ namespace Umbraco.Core.Persistence.Repositories var propertyGroups = propertyFactory.BuildEntity(dtos); return new PropertyGroupCollection(propertyGroups); } + + protected PropertyTypeCollection GetPropertyTypeCollection(int id) + { + var sql = new Sql(); + sql.Select("*") + .From() + .InnerJoin() + .On(left => left.DataTypeId, right => right.DataTypeId) + .Where(x => x.ContentTypeId == id); + + var dtos = Database.Fetch(sql); + + //TODO Move this to a PropertyTypeFactory + var list = (from dto in dtos + where (dto.PropertyTypeGroupId > 0) == false + select + new PropertyType(dto.DataTypeDto.ControlId, + dto.DataTypeDto.DbType.EnumParse(true)) + { + Alias = dto.Alias, + DataTypeDefinitionId = dto.DataTypeId, + Description = dto.Description, + Id = dto.Id, + Name = dto.Name, + HelpText = dto.HelpText, + Mandatory = dto.Mandatory, + SortOrder = dto.SortOrder + }); + + return new PropertyTypeCollection(list); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index d48ee621a3..03bb6ceca3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -37,7 +37,11 @@ namespace Umbraco.Core.Persistence.Repositories var contentTypeSql = GetBaseQuery(false); contentTypeSql.Where(GetBaseWhereClause(), new { Id = id }); - var dto = Database.Query(contentTypeSql).FirstOrDefault(); + // The SQL will contain one record for each allowed template, so order to put the default one + // at the top to populate the default template property correctly. + contentTypeSql.OrderByDescending(x => x.IsDefault); + + var dto = Database.Fetch(contentTypeSql).FirstOrDefault(); if (dto == null) return null; @@ -47,12 +51,12 @@ namespace Umbraco.Core.Persistence.Repositories contentType.AllowedContentTypes = GetAllowedContentTypeIds(id); contentType.PropertyGroups = GetPropertyGroupCollection(id); + ((ContentType)contentType).PropertyTypes = GetPropertyTypeCollection(id); var templates = Database.Fetch("WHERE contentTypeNodeId = @Id", new { Id = id }); if(templates.Any()) { - contentType.AllowedTemplates = - templates.Select(template => _templateRepository.Get(template.TemplateNodeId)); + contentType.AllowedTemplates = templates.Select(template => _templateRepository.Get(template.TemplateNodeId)).ToList(); } var list = Database.Fetch("WHERE childContentTypeId = @Id", new { Id = id}); @@ -114,16 +118,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override Sql GetBaseQuery(bool isCount) { - //TODO Investigate the proper usage of IsDefault on cmsDocumentType var sql = new Sql(); sql.Select(isCount ? "COUNT(*)" : "*") - .From() - .RightJoin() - .On(left => left.NodeId, right => right.ContentTypeNodeId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(x => x.NodeObjectType == NodeObjectTypeId) - .Where(x => x.IsDefault == true); + .From() + .RightJoin() + .On(left => left.NodeId, right => right.ContentTypeNodeId) + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .Where(x => x.NodeObjectType == NodeObjectTypeId); return sql; } @@ -170,9 +172,12 @@ namespace Umbraco.Core.Persistence.Repositories var dto = factory.BuildDto(entity); PersistNewBaseContentType(dto.ContentTypeDto, entity); - //Inserts data into the cmsDocumentType table - dto.ContentTypeNodeId = entity.Id; - Database.Insert(dto); + //Inserts data into the cmsDocumentType table if a template exists + if (dto.TemplateNodeId > 0) + { + dto.ContentTypeNodeId = entity.Id; + Database.Insert(dto); + } //Insert allowed Templates not including the default one, as that has already been inserted foreach (var template in entity.AllowedTemplates.Where(x => x != null && x.Id != dto.TemplateNodeId)) @@ -203,8 +208,11 @@ namespace Umbraco.Core.Persistence.Repositories //Look up DocumentType entries for updating - this could possibly be a "remove all, insert all"-approach Database.Delete("WHERE contentTypeNodeId = @Id", new { Id = entity.Id}); - - Database.Insert(dto); + //Insert the updated DocumentTypeDto if a template exists + if (dto.TemplateNodeId > 0) + { + Database.Insert(dto); + } //Insert allowed Templates not including the default one, as that has already been inserted foreach (var template in entity.AllowedTemplates.Where(x => x != null && x.Id != dto.TemplateNodeId)) diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index fb5342731e..8cb8e4bbda 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core.Persistence.Repositories var dataTypeSql = GetBaseQuery(false); dataTypeSql.Where(GetBaseWhereClause(), new { Id = id }); - var dataTypeDto = Database.Query(dataTypeSql).FirstOrDefault(); + var dataTypeDto = Database.Fetch(dataTypeSql).FirstOrDefault(); if (dataTypeDto == null) return null; diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index a69fe73149..4bd0e9de63 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -35,8 +35,9 @@ namespace Umbraco.Core.Persistence.Repositories protected override IDictionaryItem PerformGet(int id) { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); + var sql = GetBaseQuery(false) + .Where(GetBaseWhereClause(), new {Id = id}) + .OrderBy(x => x.UniqueId); var dto = Database.Fetch(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault(); if (dto == null) @@ -83,6 +84,7 @@ namespace Umbraco.Core.Persistence.Repositories var sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); + sql.OrderBy(x => x.UniqueId); var dtos = Database.Fetch(new DictionaryLanguageTextRelator().Map, sql); diff --git a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs index 72345c19e0..8f7c4ce1c6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs @@ -33,8 +33,8 @@ namespace Umbraco.Core.Persistence.Repositories } private void EnsureDependencies() - { - _fileSystem = FileSystemProviderManager.Current.GetFileSystemProvider("macros"); + { + _fileSystem = new PhysicalFileSystem("~/App_Data/Macros"); var serviceStackSerializer = new ServiceStackJsonSerializer(); _serializationService = new SerializationService(serviceStackSerializer); } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 56f19595a8..33a7a35e9f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -41,7 +41,7 @@ namespace Umbraco.Core.Persistence.Repositories sql.Where(GetBaseWhereClause(), new { Id = id }); sql.OrderByDescending(x => x.VersionDate); - var dto = Database.Query(sql).FirstOrDefault(); + var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return null; @@ -145,7 +145,7 @@ namespace Umbraco.Core.Persistence.Repositories sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId }); sql.OrderByDescending(x => x.VersionDate); - var dto = Database.Query(sql).FirstOrDefault(); + var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return null; @@ -240,11 +240,14 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date ((Models.Media)entity).UpdatingEntity(); - //Look up parent to get and set the correct Path if ParentId has changed + //Look up parent to get and set the correct Path and update SortOrder if ParentId has changed if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) { var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId }); entity.Path = string.Concat(parent.Path, ",", entity.Id); + entity.Level = parent.Level + 1; + var maxSortOrder = Database.ExecuteScalar("SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId", new { ParentId = entity.ParentId }); + entity.SortOrder = maxSortOrder; } var factory = new MediaFactory(NodeObjectTypeId, entity.Id); @@ -307,7 +310,7 @@ namespace Umbraco.Core.Persistence.Repositories //Loop through properties to check if the media item contains images/file that should be deleted foreach (var property in entity.Properties) { - if (property.PropertyType.DataTypeControlId == uploadFieldId && + if (property.PropertyType.DataTypeId == uploadFieldId && string.IsNullOrEmpty(property.Value.ToString()) == false && fs.FileExists(IOHelper.MapPath(property.Value.ToString()))) { diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index c4172665c5..b787bf5df8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories var contentTypeSql = GetBaseQuery(false); contentTypeSql.Where(GetBaseWhereClause(), new { Id = id}); - var dto = Database.Query(contentTypeSql).FirstOrDefault(); + var dto = Database.Fetch(contentTypeSql).FirstOrDefault(); if (dto == null) return null; @@ -43,6 +43,7 @@ namespace Umbraco.Core.Persistence.Repositories contentType.AllowedContentTypes = GetAllowedContentTypeIds(id); contentType.PropertyGroups = GetPropertyGroupCollection(id); + ((MediaType)contentType).PropertyTypes = GetPropertyTypeCollection(id); var list = Database.Fetch("WHERE childContentTypeId = @Id", new{ Id = id}); foreach (var contentTypeDto in list) diff --git a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs index dba1db3b53..9c3fa7898a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs @@ -14,8 +14,13 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class ScriptRepository : FileRepository, IScriptRepository { + internal ScriptRepository(IUnitOfWork work, IFileSystem fileSystem) + : base(work, fileSystem) + { + } + public ScriptRepository(IUnitOfWork work) - : base(work, FileSystemProviderManager.Current.GetFileSystemProvider("scripts")) + : this(work, new PhysicalFileSystem(SystemDirectories.Scripts)) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs index f9dc30e0ff..4e1ca44fb8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs @@ -14,8 +14,14 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class StylesheetRepository : FileRepository, IStylesheetRepository { + internal StylesheetRepository(IUnitOfWork work, IFileSystem fileSystem) + : base(work, fileSystem) + { + + } + public StylesheetRepository(IUnitOfWork work) - : base(work, FileSystemProviderManager.Current.GetFileSystemProvider("stylesheets")) + : this(work, new PhysicalFileSystem(SystemDirectories.Css)) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 0433418435..372f0571a0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -23,20 +23,20 @@ namespace Umbraco.Core.Persistence.Repositories private IFileSystem _masterpagesFileSystem; private IFileSystem _viewsFileSystem; - public TemplateRepository(IDatabaseUnitOfWork work) - : base(work) + public TemplateRepository(IDatabaseUnitOfWork work) + : base(work) { EnsureDepedencies(); } - public TemplateRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) - : base(work, cache) + public TemplateRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) + : base(work, cache) { EnsureDepedencies(); } - internal TemplateRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem) - : base(work, cache) + internal TemplateRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem) + : base(work, cache) { _masterpagesFileSystem = masterpageFileSystem; _viewsFileSystem = viewFileSystem; @@ -44,8 +44,8 @@ namespace Umbraco.Core.Persistence.Repositories private void EnsureDepedencies() { - _masterpagesFileSystem = FileSystemProviderManager.Current.GetFileSystemProvider("masterpages"); - _viewsFileSystem = FileSystemProviderManager.Current.GetFileSystemProvider("views"); + _masterpagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages); + _viewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews); } #region Overrides of RepositoryBase @@ -74,17 +74,20 @@ namespace Umbraco.Core.Persistence.Repositories template.MasterTemplateId = dto.Master.Value; } - if(_viewsFileSystem.FileExists(csViewName)) + if (_viewsFileSystem.FileExists(csViewName)) { PopulateViewTemplate(template, csViewName); } - else if(_viewsFileSystem.FileExists(vbViewName)) + else if (_viewsFileSystem.FileExists(vbViewName)) { PopulateViewTemplate(template, vbViewName); } else { - PopulateMasterpageTemplate(template, masterpageName); + if (_masterpagesFileSystem.FileExists(masterpageName)) + { + PopulateMasterpageTemplate(template, masterpageName); + } } return template; @@ -164,9 +167,9 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistNewItem(ITemplate entity) { - using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content))) + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content))) { - if(entity.GetTypeOfRenderingEngine() == RenderingEngine.Mvc) + if (entity.GetTypeOfRenderingEngine() == RenderingEngine.Mvc) { _viewsFileSystem.AddFile(entity.Name, stream, true); } @@ -337,7 +340,7 @@ namespace Umbraco.Core.Persistence.Repositories public IEnumerable GetAll(params string[] aliases) { - if(aliases.Any()) + if (aliases.Any()) { foreach (var id in aliases) { @@ -352,7 +355,7 @@ namespace Umbraco.Core.Persistence.Repositories yield return Get(nodeDto.NodeId); } } - + } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 57c79ef4f5..15266aae16 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -23,9 +25,16 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IEnumerable GetAllVersions(int id) { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); - sql.OrderByDescending(x => x.VersionDate); + var sql = new Sql(); + sql.Select("*") + .From() + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .Where(x => x.NodeObjectType == NodeObjectTypeId) + .Where(x => x.NodeId == id) + .OrderByDescending(x => x.VersionDate); var dtos = Database.Fetch(sql); foreach (var dto in dtos) diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs index 8439daa1c7..1d124b3687 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs @@ -41,11 +41,21 @@ namespace Umbraco.Core.Persistence.SqlSyntax public override bool DoesTableExist(Database db, string tableName) { - db.OpenSharedConnection(); - var result = - db.ExecuteScalar("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES " + - "WHERE TABLE_NAME = @TableName AND " + - "TABLE_SCHEMA = @TableSchema", new { TableName = tableName, TableSchema = db.Connection.Database }); + long result; + try + { + db.OpenSharedConnection(); + result = + db.ExecuteScalar("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES " + + "WHERE TABLE_NAME = @TableName AND " + + "TABLE_SCHEMA = @TableSchema", + new {TableName = tableName, TableSchema = db.Connection.Database}); + + } + finally + { + db.CloseSharedConnection(); + } return result > 0; } diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index a67e7960ba..60d28e5292 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -1,6 +1,7 @@ using System; using System.Data; using System.Data.Common; +using Umbraco.Core.Logging; namespace Umbraco.Core.Persistence { @@ -14,10 +15,7 @@ namespace Umbraco.Core.Persistence /// public class UmbracoDatabase : Database { - - - - private readonly Guid _instanceId = Guid.NewGuid(); + private readonly Guid _instanceId = Guid.NewGuid(); /// /// Used for testing /// @@ -41,5 +39,11 @@ namespace Umbraco.Core.Persistence public UmbracoDatabase(string connectionStringName) : base(connectionStringName) { } + + public override void OnException(Exception x) + { + LogHelper.Info(x.StackTrace); + base.OnException(x); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 42ca5fc1f5..4f1f18c2ab 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -11,6 +11,7 @@ using System.Xml.Linq; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.PropertyEditors; using umbraco.interfaces; using File = System.IO.File; @@ -458,6 +459,15 @@ namespace Umbraco.Core return ResolveTypes(); } + /// + /// Returns all available IMigrations in application + /// + /// + internal IEnumerable ResolveMigrationTypes() + { + return ResolveTypes(); + } + /// /// Gets/sets which assemblies to scan when type finding, generally used for unit testing, if not explicitly set /// this will search all assemblies known to have plugins and exclude ones known to not have them. diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index e7ef5d3d6b..23f59eaa87 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -38,4 +38,5 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("Umbraco.Core")] [assembly: InternalsVisibleTo("Umbraco.Web")] [assembly: InternalsVisibleTo("Umbraco.Web.UI")] -[assembly: InternalsVisibleTo("UmbracoExamine")] \ No newline at end of file +[assembly: InternalsVisibleTo("UmbracoExamine")] +[assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] \ No newline at end of file diff --git a/src/Umbraco.Core/Publishing/PublishingStrategy.cs b/src/Umbraco.Core/Publishing/PublishingStrategy.cs index e8d0958eec..17a80be37b 100644 --- a/src/Umbraco.Core/Publishing/PublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/PublishingStrategy.cs @@ -54,7 +54,7 @@ namespace Umbraco.Core.Publishing return false; } - content.ChangePublishedState(true); + content.ChangePublishedState(PublishedState.Published); LogHelper.Info( string.Format("Content '{0}' with Id '{1}' has been published.", @@ -106,7 +106,7 @@ namespace Umbraco.Core.Publishing continue; } - item.ChangePublishedState(true); + item.ChangePublishedState(PublishedState.Published); LogHelper.Info( string.Format("Content '{0}' with Id '{1}' has been published.", @@ -138,8 +138,8 @@ namespace Umbraco.Core.Publishing "Content '{0}' with Id '{1}' had its release date removed, because it was unpublished.", content.Name, content.Id)); } - - content.ChangePublishedState(false); + + content.ChangePublishedState(PublishedState.Unpublished); LogHelper.Info( string.Format("Content '{0}' with Id '{1}' has been unpublished.", @@ -173,7 +173,7 @@ namespace Umbraco.Core.Publishing item.Name, item.Id)); } - item.ChangePublishedState(false); + item.ChangePublishedState(PublishedState.Unpublished); LogHelper.Info( string.Format("Content '{0}' with Id '{1}' has been unpublished.", diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 958e56d40b..fb8a3168ed 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -59,15 +59,16 @@ namespace Umbraco.Core.Services _repositoryFactory = repositoryFactory; } - /// - /// Creates an object using the alias of the - /// that this Content is based on. - /// - /// Id of Parent for the new Content - /// Alias of the - /// Optional id of the user creating the content - /// - public IContent CreateContent(int parentId, string contentTypeAlias, int userId = -1) + /// + /// Creates an object using the alias of the + /// that this Content is based on. + /// + /// Name of the Content object + /// Id of Parent for the new Content + /// Alias of the + /// Optional id of the user creating the content + /// + public IContent CreateContent(string name, int parentId, string contentTypeAlias, int userId = -1) { IContentType contentType = null; IContent content = null; @@ -89,7 +90,7 @@ namespace Umbraco.Core.Services contentTypeAlias)); } - content = new Content(parentId, contentType); + content = new Content(name, parentId, contentType); if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this)) return content; @@ -245,7 +246,7 @@ namespace Umbraco.Core.Services { using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) { - var query = Query.Builder.Where(x => x.Path.StartsWith(content.Path)); + var query = Query.Builder.Where(x => x.Path.StartsWith(content.Path) && x.Id != content.Id); var contents = repository.GetByQuery(query); return contents; @@ -375,63 +376,10 @@ 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) + public bool RePublishAll(int userId = -1) { - //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")); - - 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) - { - 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(); - - 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); - } - - Audit.Add(AuditTypes.Publish, "RePublish All performed by user", userId == -1 ? 0 : userId, -1); - - return published; + return RePublishAllDo(false, userId); } /// @@ -439,12 +387,10 @@ namespace Umbraco.Core.Services /// /// The to 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 Publish(IContent content, int userId = -1, bool omitCacheRefresh = false) + public bool Publish(IContent content, int userId = -1) { - //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); + return SaveAndPublishDo(content, false, userId); } /// @@ -452,76 +398,10 @@ namespace Umbraco.Core.Services /// /// 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) + public bool PublishWithChildren(IContent content, int userId = -1) { - //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. - - //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 && IsPublishable(content) == false) - { - LogHelper.Info( - string.Format( - "Content '{0}' with Id '{1}' could not be published because its parent or one of its ancestors 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; - } - - //Consider creating a Path query instead of recursive method: - //var query = Query.Builder.Where(x => x.Path.StartsWith(content.Path)); - - var updated = new List(); - var list = new List(); - 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) - { - 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(); - - 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); - } - - return published; + return PublishWithChildrenDo(content, false, userId); } /// @@ -529,33 +409,10 @@ namespace Umbraco.Core.Services /// /// 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) + public bool UnPublish(IContent content, int userId = -1) { - //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 unpublished = _publishingStrategy.UnPublish(content, userId); - if (unpublished) - { - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateContentRepository(uow)) - { - repository.AddOrUpdate(content); - - //Remove 'published' xml from the cmsContentXml table for the unpublished content - uow.Database.Delete("WHERE nodeId = @Id", new {Id = content.Id}); - - 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); - } - - return unpublished; + return UnPublishDo(content, false, userId); } /// @@ -563,76 +420,10 @@ namespace Umbraco.Core.Services /// /// 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) + public bool SaveAndPublish(IContent content, int userId = -1) { - //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 (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) - 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 && IsPublishable(content) == 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; - } - - //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); - - 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)); - } - } - - Saved.RaiseEvent(new SaveEventArgs(content, false), this); - - //Save xml to db and call following method to fire event through PublishingStrategy to update cache - if (omitCacheRefresh == false) - _publishingStrategy.PublishingFinalized(content); - - //We need to check if children and their publish state to ensure that we republish content that was previously published - if (HasChildren(content.Id)) - { - var children = GetChildrenDeep(content.Id); - var shouldBeRepublished = children.Where(child => HasPublishedVersion(child.Id)); - - if (omitCacheRefresh == false) - _publishingStrategy.PublishingFinalized(shouldBeRepublished, false); - } - - Audit.Add(AuditTypes.Publish, "Save and Publish performed by user", userId == -1 ? 0 : userId, content.Id); - - return published; + return SaveAndPublishDo(content, false, userId); } /// @@ -642,25 +433,7 @@ namespace Umbraco.Core.Services /// Optional Id of the User saving the Content public void Save(IContent content, int userId = -1) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) - return; - - 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); - - repository.AddOrUpdate(content); - uow.Commit(); - } - - Saved.RaiseEvent(new SaveEventArgs(content, false), this); - - Audit.Add(AuditTypes.Save, "Save Content performed by user", userId == -1 ? 0 : userId, content.Id); + Save(content, true, userId); } /// @@ -690,7 +463,7 @@ namespace Umbraco.Core.Services //Only change the publish state if the "previous" version was actually published if (content.Published) - content.ChangePublishedState(false); + content.ChangePublishedState(PublishedState.Saved); repository.AddOrUpdate(content); uow.Commit(); @@ -893,52 +666,90 @@ namespace Umbraco.Core.Services } /// - /// Moves an object to a new location by changing its parent id. - /// - /// - /// If the object is already published it will be - /// published after being moved to its new location. Otherwise it'll just - /// be saved with a new parent id. - /// - /// The to move - /// Id of the Content's new Parent - /// Optional Id of the User moving the Content - public void Move(IContent content, int parentId, int userId = -1) - { - //TODO Verify that SortOrder + Path is updated correctly - //TODO Add a check to see if parentId = -20 because then we should change the TrashState - - if (Moving.IsRaisedEventCancelled(new MoveEventArgs(content, parentId), this)) - return; - - SetWriter(content, userId); + /// Moves an object to a new location by changing its parent id. + /// + /// + /// If the object is already published it will be + /// published after being moved to its new location. Otherwise it'll just + /// be saved with a new parent id. + /// + /// The to move + /// Id of the Content's new Parent + /// Optional Id of the User moving the Content + public void Move(IContent content, int parentId, int userId = -1) + { + //This ensures that the correct method is called if this method is used to Move to recycle bin. + if (parentId == -20) + { + MoveToRecycleBin(content, userId); + return; + } - //If Content is being moved away from Recycle Bin, its state should be un-trashed - if (content.Trashed && parentId != -20) - { - content.ChangeTrashedState(false, parentId); - } - else - { - content.ParentId = parentId; - } + if (Moving.IsRaisedEventCancelled(new MoveEventArgs(content, parentId), this)) + return; - //If Content is published, it should be (re)published from its new location - if (content.Published) - { - SaveAndPublish(content, userId); - } - else - { - Save(content, userId); - } + SetWriter(content, userId); + var parent = GetById(parentId); + content.Path = string.Concat(parent.Path, ",", content.Id); + content.Level = parent.Level + 1; - Moved.RaiseEvent(new MoveEventArgs(content, false, parentId), this); + //If Content is being moved away from Recycle Bin, its state should be un-trashed + if (content.Trashed && parentId != -20) + { + content.ChangeTrashedState(false, parentId); + } + else + { + content.ParentId = parentId; + } + + //If Content is published, it should be (re)published from its new location + if (content.Published) + { + //If Content is Publishable its saved and published + //otherwise we save the content without changing the publish state, and generate new xml because the Path, Level and Parent has changed. + if (IsPublishable(content)) + { + SaveAndPublish(content, userId); + } + else + { + Save(content, false, userId); + + using (var uow = _uowProvider.GetUnitOfWork()) + { + 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)); + } + } + } + else + { + Save(content, userId); + } + + //Ensure that Path and Level is updated on children + var children = GetChildren(content.Id); + if (children.Any()) + { + foreach (var child in children) + { + Move(child, content.Id, userId); + } + } + + Moved.RaiseEvent(new MoveEventArgs(content, false, parentId), this); Audit.Add(AuditTypes.Move, "Move Content performed by user", userId == -1 ? 0 : userId, content.Id); } - /// + /// /// Empties the Recycle Bin by deleting all that resides in the bin /// public void EmptyRecycleBin() @@ -982,7 +793,7 @@ namespace Umbraco.Core.Services // A copy should never be set to published // automatically even if the original was - this.UnPublish(copy); + this.UnPublish(copy, userId); if (Copying.IsRaisedEventCancelled(new CopyEventArgs(content, copy, parentId), this)) return null; @@ -996,13 +807,13 @@ namespace Umbraco.Core.Services uow.Commit(); var uploadFieldId = new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c"); - if (content.Properties.Any(x => x.PropertyType.DataTypeControlId == uploadFieldId)) + if (content.Properties.Any(x => x.PropertyType.DataTypeId == uploadFieldId)) { bool isUpdated = false; var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); //Loop through properties to check if the content contains media that should be deleted - foreach (var property in content.Properties.Where(x => x.PropertyType.DataTypeControlId == uploadFieldId + foreach (var property in content.Properties.Where(x => x.PropertyType.DataTypeId == uploadFieldId && string.IsNullOrEmpty(x.Value.ToString()) == false)) { if (fs.FileExists(IOHelper.MapPath(property.Value.ToString()))) @@ -1133,10 +944,344 @@ namespace Umbraco.Core.Services { _httpContext = httpContext; } + + /// + /// Internal method to Re-Publishes all Content for legacy purposes. + /// + /// 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 not update the cache. + /// True if publishing succeeded, otherwise False + internal bool RePublishAll(bool omitCacheRefresh = true, int userId = -1) + { + return RePublishAllDo(omitCacheRefresh, userId); + } + + /// + /// Internal method that Publishes a single object for legacy purposes. + /// + /// The to publish + /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will not update the cache. + /// Optional Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + internal bool Publish(IContent content, bool omitCacheRefresh = true, int userId = -1) + { + return SaveAndPublishDo(content, omitCacheRefresh, userId); + } + + /// + /// Internal method that Publishes a object and all its children for legacy purposes. + /// + /// The to publish along with its children + /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will not update the cache. + /// Optional Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + internal bool PublishWithChildren(IContent content, bool omitCacheRefresh = true, int userId = -1) + { + return PublishWithChildrenDo(content, omitCacheRefresh, userId); + } + + /// + /// Internal method that UnPublishes a single object for legacy purposes. + /// + /// The to publish + /// Optional boolean to avoid having the cache refreshed when calling this Unpublish method. By default this method will not update the cache. + /// Optional Id of the User issueing the publishing + /// True if unpublishing succeeded, otherwise False + internal bool UnPublish(IContent content, bool omitCacheRefresh = true, int userId = -1) + { + return UnPublishDo(content, omitCacheRefresh, userId); + } + + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will not update the cache. + /// Optional Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + internal bool SaveAndPublish(IContent content, bool omitCacheRefresh = true, int userId = -1) + { + return SaveAndPublishDo(content, omitCacheRefresh, userId); + } + #endregion #region Private Methods + /// + /// 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 + private bool RePublishAllDo(bool omitCacheRefresh = false, int userId = -1) + { + 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")); + + 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) + { + 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(); + + 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); + } + + Audit.Add(AuditTypes.Publish, "RePublish All performed by user", userId == -1 ? 0 : userId, -1); + + return published; + } + + /// + /// Publishes a object and all its children + /// + /// The to publish along with its children + /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will update the cache. + /// Optional Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + private bool PublishWithChildrenDo(IContent content, bool omitCacheRefresh = false, int userId = -1) + { + //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 && IsPublishable(content) == false) + { + LogHelper.Info( + string.Format( + "Content '{0}' with Id '{1}' could not be published because its parent or one of its ancestors 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; + } + + //Consider creating a Path query instead of recursive method: + //var query = Query.Builder.Where(x => x.Path.StartsWith(content.Path)); + + var updated = new List(); + var list = new List(); + 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) + { + 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(); + + 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); + } + + return published; + } + + /// + /// UnPublishes a single object + /// + /// The to publish + /// Optional boolean to avoid having the cache refreshed when calling this Unpublish method. By default this method will update the cache. + /// Optional Id of the User issueing the publishing + /// True if unpublishing succeeded, otherwise False + private bool UnPublishDo(IContent content, bool omitCacheRefresh = false, int userId = -1) + { + var unpublished = _publishingStrategy.UnPublish(content, userId); + if (unpublished) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + repository.AddOrUpdate(content); + + //Remove 'published' xml from the cmsContentXml table for the unpublished content + uow.Database.Delete("WHERE nodeId = @Id", new { Id = content.Id }); + + 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); + } + + return unpublished; + } + + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional boolean to avoid having the cache refreshed when calling this Publish method. By default this method will update the cache. + /// Optional Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + private bool SaveAndPublishDo(IContent content, bool omitCacheRefresh = false, int userId = -1) + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) + 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 && IsPublishable(content) == 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; + } + + //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); + + 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)); + } + } + + Saved.RaiseEvent(new SaveEventArgs(content, false), this); + + //Save xml to db and call following method to fire event through PublishingStrategy to update cache + if (omitCacheRefresh == false) + _publishingStrategy.PublishingFinalized(content); + + //We need to check if children and their publish state to ensure that we republish content that was previously published + if (HasChildren(content.Id)) + { + var children = GetChildrenDeep(content.Id); + var shouldBeRepublished = children.Where(child => HasPublishedVersion(child.Id)); + + if (omitCacheRefresh == false) + _publishingStrategy.PublishingFinalized(shouldBeRepublished, false); + } + + Audit.Add(AuditTypes.Publish, "Save and Publish performed by user", userId == -1 ? 0 : userId, content.Id); + + return published; + } + + /// + /// Saves a single object + /// + /// The to save + /// Boolean indicating whether or not to change the Published state upon saving + /// Optional Id of the User saving the Content + private void Save(IContent content, bool changeState, int userId = -1) + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) + return; + + 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 (changeState && content.Published) + content.ChangePublishedState(PublishedState.Saved); + + repository.AddOrUpdate(content); + uow.Commit(); + } + + Saved.RaiseEvent(new SaveEventArgs(content, false), this); + + Audit.Add(AuditTypes.Save, "Save Content performed by user", userId == -1 ? 0 : userId, content.Id); + } + /// /// Gets a flat list of decendents of content from parent id /// @@ -1189,7 +1334,7 @@ namespace Umbraco.Core.Services if (checkCurrent == false && id == content.Id) continue; //Check if the content for the current id is published - escape the loop if we encounter content that isn't published - var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.GetById(id).Published; + var hasPublishedVersion = HasPublishedVersion(id); if (hasPublishedVersion == false) return false; } diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 57ad05f15f..49ad5953a4 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -153,7 +153,7 @@ namespace Umbraco.Core.Services using (var repository = _repositoryFactory.CreateContentTypeRepository(uow)) { //Find ContentTypes using this IDataTypeDefinition on a PropertyType - var query = Query.Builder.Where(x => x.DataTypeId == dataTypeDefinition.Id); + var query = Query.Builder.Where(x => x.DataTypeDefinitionId == dataTypeDefinition.Id); var contentTypes = repository.GetByQuery(query); //Loop through the list of results and remove the PropertyTypes that references the DataTypeDefinition that is being deleted @@ -163,7 +163,7 @@ namespace Umbraco.Core.Services foreach (var group in contentType.PropertyGroups) { - var types = @group.PropertyTypes.Where(x => x.DataTypeId == dataTypeDefinition.Id); + var types = @group.PropertyTypes.Where(x => x.DataTypeDefinitionId == dataTypeDefinition.Id); foreach (var propertyType in types) { @group.PropertyTypes.Remove(propertyType); diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 4e4a400b51..1f902ed266 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -13,11 +13,12 @@ namespace Umbraco.Core.Services /// Creates an object using the alias of the /// that this Content is based on. /// + /// Name of the Content object /// Id of Parent for the new Content /// Alias of the /// Optional id of the user creating the content /// - IContent CreateContent(int parentId, string contentTypeAlias, int userId = -1); + IContent CreateContent(string name, int parentId, string contentTypeAlias, int userId = -1); /// /// Gets an object by Id @@ -209,45 +210,40 @@ 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 - bool RePublishAll(int userId = -1, bool omitCacheRefresh = false); + bool RePublishAll(int userId = -1); /// /// Publishes 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 Publish method. By default this method will update the cache. /// True if publishing succeeded, otherwise False - bool Publish(IContent content, int userId = -1, bool omitCacheRefresh = false); + bool Publish(IContent content, int userId = -1); /// /// 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 - bool PublishWithChildren(IContent content, int userId = -1, bool omitCacheRefresh = false); + bool PublishWithChildren(IContent content, int userId = -1); /// /// 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 - bool UnPublish(IContent content, int userId = -1, bool omitCacheRefresh = false); + bool UnPublish(IContent content, int userId = -1); /// /// 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 - bool SaveAndPublish(IContent content, int userId = -1, bool omitCacheRefresh = false); + bool SaveAndPublish(IContent content, int userId = -1); /// /// Permanently deletes an object. diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 2e240bbce9..c7cecb9d02 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -13,11 +13,12 @@ namespace Umbraco.Core.Services /// Creates an object using the alias of the /// that this Media is based on. /// + /// Name of the Media object /// Id of Parent for the new Media item /// Alias of the /// Optional id of the user creating the media item /// - IMedia CreateMedia(int parentId, string mediaTypeAlias, int userId = -1); + IMedia CreateMedia(string name, int parentId, string mediaTypeAlias, int userId = -1); /// /// Gets an object by Id diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 492fa4a77f..bb5d0ac844 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -44,11 +44,12 @@ namespace Umbraco.Core.Services /// Creates an object using the alias of the /// that this Media is based on. /// + /// Name of the Media object /// 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) + public IMedia CreateMedia(string name, int parentId, string mediaTypeAlias, int userId = -1) { IMediaType mediaType = null; var uow = _uowProvider.GetUnitOfWork(); @@ -68,7 +69,7 @@ namespace Umbraco.Core.Services mediaTypeAlias)); } - var media = new Models.Media(parentId, mediaType); + var media = new Models.Media(name, parentId, mediaType); if (Creating.IsRaisedEventCancelled(new NewEventArgs(media, mediaTypeAlias, parentId), this)) return media; @@ -262,12 +263,29 @@ namespace Umbraco.Core.Services /// Id of the User moving the Media public void Move(IMedia media, int parentId, int userId = -1) { + //This ensures that the correct method is called if this method is used to Move to recycle bin. + if (parentId == -21) + { + MoveToRecycleBin(media, userId); + return; + } + if (Moving.IsRaisedEventCancelled(new MoveEventArgs(media, parentId), this)) return; media.ParentId = parentId; Save(media, userId); + //Ensure that Path and Level is updated on children + var children = GetChildren(media.Id); + if (children.Any()) + { + var parentPath = media.Path; + var parentLevel = media.Level; + var updatedDescendents = UpdatePathAndLevelOnChildren(children, parentPath, parentLevel); + Save(updatedDescendents, userId); + } + Moved.RaiseEvent(new MoveEventArgs(media, false, parentId), this); Audit.Add(AuditTypes.Move, "Move Media performed by user", userId == -1 ? 0 : userId, media.Id); @@ -280,14 +298,20 @@ namespace Umbraco.Core.Services /// 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)) + if (Trashing.IsRaisedEventCancelled(new MoveEventArgs(media, -21), this)) return; + //Move children to Recycle Bin before the 'possible parent' is moved there + var children = GetChildren(media.Id); + foreach (var child in children) + { + MoveToRecycleBin(child, userId); + } + var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMediaRepository(uow)) { - ((Core.Models.Media)media).ChangeTrashedState(true); + media.ChangeTrashedState(true); repository.AddOrUpdate(media); uow.Commit(); } @@ -499,6 +523,32 @@ namespace Umbraco.Core.Services _httpContext = httpContext; } + /// + /// Updates the Path and Level on a collection of objects + /// based on the Parent's Path and Level. + /// + /// Collection of objects to update + /// Path of the Parent media + /// Level of the Parent media + /// Collection of updated objects + private List UpdatePathAndLevelOnChildren(IEnumerable children, string parentPath, int parentLevel) + { + var list = new List(); + foreach (var child in children) + { + child.Path = string.Concat(parentPath, ",", child.Id); + child.Level = parentLevel + 1; + list.Add(child); + + var grandkids = GetChildren(child.Id); + if (grandkids.Any()) + { + list.AddRange(UpdatePathAndLevelOnChildren(grandkids, child.Path, child.Level)); + } + } + return list; + } + /// /// Updates a media object with the User (id), who created the content. /// diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 5be5256bb9..40f8648165 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -11,6 +11,7 @@ using System.Text.RegularExpressions; using System.Web; using System.Xml; using Umbraco.Core.Configuration; +using System.Web.Security; namespace Umbraco.Core { @@ -20,47 +21,57 @@ namespace Umbraco.Core /// public static class StringExtensions { - //this is from SqlMetal and just makes it a bit of fun to allow pluralisation - public static string MakePluralName(this string name) - { - if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("ss", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase))) - { - name = name + "es"; - return name; - } - if ((name.EndsWith("y", StringComparison.OrdinalIgnoreCase) && (name.Length > 1)) && !IsVowel(name[name.Length - 2])) - { - name = name.Remove(name.Length - 1, 1); - name = name + "ies"; - return name; - } - if (!name.EndsWith("s", StringComparison.OrdinalIgnoreCase)) - { - name = name + "s"; - } - return name; - } + public static string EncryptWithMachineKey(this string toEncrypt) + { + var output = FormsAuthentication.Encrypt(new FormsAuthenticationTicket(0, "temp", DateTime.Now, DateTime.MaxValue, false, toEncrypt)); + return output; + } + public static string DecryptWithMachineKey(this string encrypted) + { + var output = FormsAuthentication.Decrypt(encrypted); + return output.UserData; + } + //this is from SqlMetal and just makes it a bit of fun to allow pluralisation + public static string MakePluralName(this string name) + { + if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("ss", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase))) + { + name = name + "es"; + return name; + } + if ((name.EndsWith("y", StringComparison.OrdinalIgnoreCase) && (name.Length > 1)) && !IsVowel(name[name.Length - 2])) + { + name = name.Remove(name.Length - 1, 1); + name = name + "ies"; + return name; + } + if (!name.EndsWith("s", StringComparison.OrdinalIgnoreCase)) + { + name = name + "s"; + } + return name; + } - public static bool IsVowel(this char c) - { - switch (c) - { - case 'O': - case 'U': - case 'Y': - case 'A': - case 'E': - case 'I': - case 'o': - case 'u': - case 'y': - case 'a': - case 'e': - case 'i': - return true; - } - return false; - } + public static bool IsVowel(this char c) + { + switch (c) + { + case 'O': + case 'U': + case 'Y': + case 'A': + case 'E': + case 'I': + case 'o': + case 'u': + case 'y': + case 'a': + case 'e': + case 'i': + return true; + } + return false; + } /// /// Trims the specified value from a string; accepts a string input whereas the in-built implementation only accepts char or char[]. @@ -74,49 +85,49 @@ namespace Umbraco.Core return value.TrimEnd(forRemoving).TrimStart(forRemoving); } - public static string EncodeJsString(this string s) - { - var sb = new StringBuilder(); - foreach (var c in s) - { - switch (c) - { - case '\"': - sb.Append("\\\""); - break; - case '\\': - sb.Append("\\\\"); - break; - case '\b': - sb.Append("\\b"); - break; - case '\f': - sb.Append("\\f"); - break; - case '\n': - sb.Append("\\n"); - break; - case '\r': - sb.Append("\\r"); - break; - case '\t': - sb.Append("\\t"); - break; - default: - int i = (int)c; - if (i < 32 || i > 127) - { - sb.AppendFormat("\\u{0:X04}", i); - } - else - { - sb.Append(c); - } - break; - } - } - return sb.ToString(); - } + public static string EncodeJsString(this string s) + { + var sb = new StringBuilder(); + foreach (var c in s) + { + switch (c) + { + case '\"': + sb.Append("\\\""); + break; + case '\\': + sb.Append("\\\\"); + break; + case '\b': + sb.Append("\\b"); + break; + case '\f': + sb.Append("\\f"); + break; + case '\n': + sb.Append("\\n"); + break; + case '\r': + sb.Append("\\r"); + break; + case '\t': + sb.Append("\\t"); + break; + default: + int i = (int)c; + if (i < 32 || i > 127) + { + sb.AppendFormat("\\u{0:X04}", i); + } + else + { + sb.Append(c); + } + break; + } + } + return sb.ToString(); + } public static string TrimEnd(this string value, string forRemoving) { @@ -144,25 +155,25 @@ namespace Umbraco.Core return toStartWith + input.TrimStart(toStartWith.ToArray()); // Ensure each char is removed first from input, e.g. ~/ plus /Path will equal ~/Path not ~//Path } - public static string EnsureStartsWith(this string input, char value) - { - return input.StartsWith(value.ToString()) ? input : value + input; - } + public static string EnsureStartsWith(this string input, char value) + { + return input.StartsWith(value.ToString()) ? input : value + input; + } - public static string EnsureEndsWith(this string input, char value) - { - return input.EndsWith(value.ToString()) ? input : input + value; - } + public static string EnsureEndsWith(this string input, char value) + { + return input.EndsWith(value.ToString()) ? input : input + value; + } public static bool IsLowerCase(this char ch) { return ch.ToString(CultureInfo.InvariantCulture) == ch.ToString(CultureInfo.InvariantCulture).ToLower(); } - public static bool IsUpperCase(this char ch) - { - return ch.ToString(CultureInfo.InvariantCulture) == ch.ToString(CultureInfo.InvariantCulture).ToUpper(); - } + public static bool IsUpperCase(this char ch) + { + return ch.ToString(CultureInfo.InvariantCulture) == ch.ToString(CultureInfo.InvariantCulture).ToUpper(); + } /// Is null or white space. /// The str. @@ -479,10 +490,10 @@ namespace Umbraco.Core return String.Equals(compare, compareTo, StringComparison.InvariantCultureIgnoreCase); } - public static bool InvariantStartsWith(this string compare, string compareTo) - { - return compare.StartsWith(compareTo, StringComparison.InvariantCultureIgnoreCase); - } + public static bool InvariantStartsWith(this string compare, string compareTo) + { + return compare.StartsWith(compareTo, StringComparison.InvariantCultureIgnoreCase); + } public static bool InvariantContains(this string compare, string compareTo) { @@ -697,7 +708,7 @@ namespace Umbraco.Core .TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; return currentFolder; } - + /// /// Truncates the specified text string. @@ -760,5 +771,54 @@ namespace Umbraco.Core return newUrl; } + + /// + /// An extention method to ensure that an Alias string doesn't contains any illegal characters + /// which is defined in a private constant 'ValidCharacters' in this class. + /// Conventions over configuration, baby. You can't touch this - MC Hammer! + /// + /// + /// Copied and cleaned up a bit from umbraco.cms.helpers.Casing. + /// + /// The alias. + /// An alias guaranteed not to contain illegal characters + public static string ToSafeAlias(this string alias) + { + const string validAliasCharacters = "_-abcdefghijklmnopqrstuvwxyz1234567890"; + const string invalidFirstCharacters = "01234567890"; + var safeString = new StringBuilder(); + int aliasLength = alias.Length; + for (int i = 0; i < aliasLength; i++) + { + string currentChar = alias.Substring(i, 1); + if (validAliasCharacters.Contains(currentChar.ToLower())) + { + // check for camel (if previous character is a space, we'll upper case the current one + if (safeString.Length == 0 && invalidFirstCharacters.Contains(currentChar.ToLower())) + { + currentChar = ""; + } + else + { + if (i < aliasLength - 1 && i > 0 && alias.Substring(i - 1, 1) == " ") + currentChar = currentChar.ToUpper(); + + safeString.Append(currentChar); + } + } + } + + return safeString.ToString(); + } + + public static string ToSafeAliasWithForcingCheck(this string alias) + { + if (UmbracoSettings.ForceSafeAliases) + { + return alias.ToSafeAlias(); + } + + return alias; + } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 6e3e0d2271..2b556f637f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -31,6 +31,7 @@ TRACE prompt 4 + bin\Release\Umbraco.Core.xml @@ -118,6 +119,7 @@ + @@ -125,6 +127,7 @@ + @@ -181,6 +184,7 @@ + @@ -265,8 +269,8 @@ + - @@ -372,18 +376,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/src/Umbraco.Core/XmlHelper.cs b/src/Umbraco.Core/XmlHelper.cs index 0a3d16e9c8..27f4d0e923 100644 --- a/src/Umbraco.Core/XmlHelper.cs +++ b/src/Umbraco.Core/XmlHelper.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core /// The text. /// The XML doc. /// - internal static XmlNode ImportXmlNodeFromText(string text, ref XmlDocument xmlDoc) + public static XmlNode ImportXmlNodeFromText(string text, ref XmlDocument xmlDoc) { xmlDoc.LoadXml(text); return xmlDoc.FirstChild; @@ -80,7 +80,7 @@ namespace Umbraco.Core /// The node name. /// The node value. /// A XmlNode - public static XmlNode AddCDataNode(XmlDocument xd, string name, string value) + public static XmlNode AddCDataNode(XmlDocument xd, string name, string value) { var temp = xd.CreateNode(XmlNodeType.Element, name, ""); temp.AppendChild(xd.CreateCDataSection(value)); @@ -92,7 +92,7 @@ namespace Umbraco.Core /// /// The XmlNode. /// the value as a string - internal static string GetNodeValue(XmlNode n) + public static string GetNodeValue(XmlNode n) { var value = string.Empty; if (n == null || n.FirstChild == null) @@ -108,7 +108,7 @@ namespace Umbraco.Core /// /// true if the specified string appears to be XML; otherwise, false. /// - internal static bool CouldItBeXml(string xml) + public static bool CouldItBeXml(string xml) { if (!string.IsNullOrEmpty(xml)) { @@ -131,7 +131,7 @@ namespace Umbraco.Core /// Name of the root. /// Name of the element. /// Returns an System.Xml.XmlDocument representation of the delimited string data. - internal static XmlDocument Split(string data, string[] separator, string rootName, string elementName) + public static XmlDocument Split(string data, string[] separator, string rootName, string elementName) { return Split(new XmlDocument(), data, separator, rootName, elementName); } @@ -145,7 +145,7 @@ namespace Umbraco.Core /// Name of the root node. /// Name of the element node. /// Returns an System.Xml.XmlDocument representation of the delimited string data. - internal static XmlDocument Split(XmlDocument xml, string data, string[] separator, string rootName, string elementName) + public static XmlDocument Split(XmlDocument xml, string data, string[] separator, string rootName, string elementName) { // load new XML document. xml.LoadXml(string.Concat("<", rootName, "/>")); @@ -174,7 +174,7 @@ namespace Umbraco.Core /// /// /// - internal static Dictionary GetAttributesFromElement(string tag) + public static Dictionary GetAttributesFromElement(string tag) { var m = Regex.Matches(tag, "(?\\S*)=\"(?[^\"]*)\"", diff --git a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs index e69fd7f3dd..06c62a9de2 100644 --- a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs +++ b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.BusinessLogic private void ClearDatabase() { var databaseSettings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; - var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString) as SqlCEHelper; + var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false) as SqlCEHelper; if (dataHelper == null) throw new InvalidOperationException("The sql helper for unit tests must be of type SqlCEHelper, check the ensure the connection string used for this test is set to use SQLCE"); @@ -57,7 +57,7 @@ namespace Umbraco.Tests.BusinessLogic AppDomain.CurrentDomain.SetData("DataDirectory", TestHelper.CurrentAssemblyDirectory); var databaseSettings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; - var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString) as SqlCEHelper; + var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false) as SqlCEHelper; var installer = dataHelper.Utility.CreateInstaller(); if (installer.CanConnect) diff --git a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs index eaced2de55..570f73678e 100644 --- a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs +++ b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs @@ -29,6 +29,7 @@ namespace Umbraco.Tests.ContentStores PublishedMediaTests.DoTearDown(); } + [Ignore] [Test] public void Get_Root_Docs() { @@ -47,6 +48,7 @@ namespace Umbraco.Tests.ContentStores } + [Ignore] [Test] public void Get_Item_Without_Examine() { diff --git a/src/Umbraco.Tests/GlobalSettingsTests.cs b/src/Umbraco.Tests/GlobalSettingsTests.cs index f411a55a93..98901d49f4 100644 --- a/src/Umbraco.Tests/GlobalSettingsTests.cs +++ b/src/Umbraco.Tests/GlobalSettingsTests.cs @@ -30,6 +30,7 @@ namespace Umbraco.Tests ConfigurationManager.AppSettings.Set("umbracoReservedUrls", ""); } + [Ignore] [Test] public void Is_Version_From_Assembly_Correct() { diff --git a/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs b/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs index c48cee58d7..17caa76dfc 100644 --- a/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs @@ -110,6 +110,7 @@ namespace Umbraco.Tests.IO _fileSystem.DeleteDirectory("test", true); } + [Ignore] [Test] public void Can_Get_File_Dates() { diff --git a/src/Umbraco.Tests/IO/IOHelperTest.cs b/src/Umbraco.Tests/IO/IOHelperTest.cs index d23d5cba5f..9f8822fbff 100644 --- a/src/Umbraco.Tests/IO/IOHelperTest.cs +++ b/src/Umbraco.Tests/IO/IOHelperTest.cs @@ -13,6 +13,13 @@ namespace Umbraco.Tests.IO public class IOHelperTest { + [Test] + public void IOHelper_ResolveUrl() + { + var result = IOHelper.ResolveUrl("~/Scripts"); + Assert.AreEqual("/Scripts", result); + } + /// ///A test for MapPath verifying that HttpContext method (which includes vdirs) matches non-HttpContext method /// diff --git a/src/Umbraco.Tests/LegacyApi/MediaTypeTests.cs b/src/Umbraco.Tests/LegacyApi/MediaTypeTests.cs index 6f55b1630f..2d187b35f6 100644 --- a/src/Umbraco.Tests/LegacyApi/MediaTypeTests.cs +++ b/src/Umbraco.Tests/LegacyApi/MediaTypeTests.cs @@ -3,6 +3,8 @@ using System.Linq; using NUnit.Framework; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; +using umbraco.BusinessLogic; +using umbraco.cms.businesslogic.datatype; using umbraco.cms.businesslogic.media; namespace Umbraco.Tests.LegacyApi @@ -37,6 +39,49 @@ namespace Umbraco.Tests.LegacyApi Assert.That(updated.AllowedChildContentTypeIDs.Any(x => x == 1045), Is.True); } + [Test] + public void Can_Set_Tab_On_PropertyType() + { + var UPLOAD_DATATYPE_ID = -90; + var CROP_DATATYPE_ID = 1043; + var LABEL_DATATYPE_ID = -92; + var mediaTypeName = "ImageWide"; + + MediaType mediaType = MediaType.MakeNew(new User(0), mediaTypeName); + + int imageTab = mediaType.AddVirtualTab("Image"); + int cropTab = mediaType.AddVirtualTab("Crop"); + + mediaType.AddPropertyType(new DataTypeDefinition(UPLOAD_DATATYPE_ID), "umbracoFile", "Upload image"); + mediaType.AddPropertyType(new DataTypeDefinition(LABEL_DATATYPE_ID), "umbracoWidth", "Width"); + mediaType.AddPropertyType(new DataTypeDefinition(LABEL_DATATYPE_ID), "umbracoHeight", "Height"); + mediaType.AddPropertyType(new DataTypeDefinition(LABEL_DATATYPE_ID), "umbracoBytes", "Size"); + mediaType.AddPropertyType(new DataTypeDefinition(LABEL_DATATYPE_ID), "umbracoExtension", "Type"); + mediaType.AddPropertyType(new DataTypeDefinition(CROP_DATATYPE_ID), "wideImage", "Wide image"); + + mediaType.SetTabOnPropertyType(mediaType.getPropertyType("umbracoFile"), imageTab); + mediaType.SetTabOnPropertyType(mediaType.getPropertyType("umbracoWidth"), imageTab); + mediaType.SetTabOnPropertyType(mediaType.getPropertyType("umbracoHeight"), imageTab); + mediaType.SetTabOnPropertyType(mediaType.getPropertyType("umbracoBytes"), imageTab); + mediaType.SetTabOnPropertyType(mediaType.getPropertyType("umbracoExtension"), imageTab); + mediaType.SetTabOnPropertyType(mediaType.getPropertyType("wideImage"), cropTab); + + mediaType.Text = mediaTypeName; + mediaType.IconUrl = "mediaPhoto.gif"; + mediaType.Save(); + + Assert.That(mediaType.getVirtualTabs.Count(), Is.EqualTo(2)); + Assert.That(mediaType.getVirtualTabs.Any(x => x.Caption.Equals("Image")), Is.True); + Assert.That(mediaType.getVirtualTabs.Any(x => x.Caption.Equals("Crop")), Is.True); + + var updated = new MediaType(mediaType.Id); + Assert.That(updated.getVirtualTabs.Count(), Is.EqualTo(2)); + Assert.That(updated.getVirtualTabs.Any(x => x.Caption.Equals("Image")), Is.True); + Assert.That(updated.getVirtualTabs.Any(x => x.Caption.Equals("Crop")), Is.True); + Assert.That(updated.ContentTypeItem.PropertyGroups.Count(), Is.EqualTo(2)); + Assert.That(updated.ContentTypeItem.PropertyTypes.Count(), Is.EqualTo(6)); + } + public void CreateTestData() { //Create and Save ContentType "video" -> 1045 diff --git a/src/Umbraco.Tests/Migrations/AlterMigrationTests.cs b/src/Umbraco.Tests/Migrations/AlterMigrationTests.cs index 3ebe04a48d..a752e94662 100644 --- a/src/Umbraco.Tests/Migrations/AlterMigrationTests.cs +++ b/src/Umbraco.Tests/Migrations/AlterMigrationTests.cs @@ -21,7 +21,7 @@ namespace Umbraco.Tests.Migrations public void Can_Get_Up_Migration_From_MigrationStub() { // Arrange - var context = new MigrationContext(DatabaseProviders.SqlServerCE); + var context = new MigrationContext(DatabaseProviders.SqlServerCE, null); var stub = new AlterUserTableMigrationStub(); // Act diff --git a/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs b/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs index c148ec222e..7601aa2f59 100644 --- a/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs +++ b/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.ObjectResolution; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.SqlSyntax; @@ -19,31 +20,25 @@ namespace Umbraco.Tests.Migrations { TestHelper.SetupLog4NetForTests(); - //this ensures its reset - PluginManager.Current = new PluginManager(false); + MigrationResolver.Current = new MigrationResolver(new List + { + typeof (AlterUserTableMigrationStub), + typeof(Dummy), + typeof (FourNineMigration), + typeof (FourTenMigration), + typeof (FourElevenMigration), + typeof (FourElevenMigration) + }); - //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver - PluginManager.Current.AssembliesToScan = new[] - { - typeof (FourNineMigration).Assembly - }; + Resolution.Freeze(); SyntaxConfig.SqlSyntaxProvider = SqlCeSyntax.Provider; - } - - [Test] - public void Can_Find_Migrations_In_Current_Assembly() - { - var foundTypes = PluginManager.Current.ResolveMigrationTypes(); - - Assert.That(foundTypes.Any(), Is.True); - Assert.That(foundTypes.Count(), Is.EqualTo(4)); - } + } [Test] public void Can_Find_Migrations_With_Targtet_Version_Six() { - var foundMigrations = PluginManager.Current.FindMigrations(); + var foundMigrations = MigrationResolver.Current.Migrations; var targetVersion = new Version("6.0.0"); var list = new List(); @@ -61,7 +56,7 @@ namespace Umbraco.Tests.Migrations Assert.That(list.Count, Is.EqualTo(3)); - var context = new MigrationContext(DatabaseProviders.SqlServerCE); + var context = new MigrationContext(DatabaseProviders.SqlServerCE, null); foreach (MigrationBase migration in list) { migration.GetUpExpressions(context); @@ -78,8 +73,9 @@ namespace Umbraco.Tests.Migrations [TearDown] public void TearDown() - { - PluginManager.Current = null; + { + MigrationResolver.Reset(); + Resolution.IsFrozen = false; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Migrations/PluginManagerExtensions.cs b/src/Umbraco.Tests/Migrations/PluginManagerExtensions.cs deleted file mode 100644 index 77b63703b0..0000000000 --- a/src/Umbraco.Tests/Migrations/PluginManagerExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core; -using Umbraco.Core.Persistence.Migrations; - -namespace Umbraco.Tests.Migrations -{ - /// - /// Used for TypeInheritanceTest and CodeFirstTests - /// - internal static class PluginManagerExtensions - { - public static IEnumerable ResolveMigrationTypes(this PluginManager resolver) - { - return resolver.ResolveTypesWithAttribute(); - } - - public static IEnumerable FindMigrations(this PluginManager resolver) - { - var types = resolver.ResolveTypesWithAttribute(); - return resolver.CreateInstances(types); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Tests/Migrations/SqlScripts/SqlCe-SchemaAndData-4110.sql b/src/Umbraco.Tests/Migrations/SqlScripts/SqlCe-SchemaAndData-4110.sql new file mode 100644 index 0000000000..82f4eefcca Binary files /dev/null and b/src/Umbraco.Tests/Migrations/SqlScripts/SqlCe-SchemaAndData-4110.sql differ diff --git a/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.Designer.cs b/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.Designer.cs index cb4f12e103..f0c8329aa1 100644 --- a/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.Designer.cs +++ b/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.Designer.cs @@ -86,23 +86,47 @@ namespace Umbraco.Tests.Migrations.SqlScripts { } /// - /// Looks up a localized string similar to /******************************************************************************************* + /// Looks up a localized string similar to -- Script Date: 10-01-2013 11:50 - Generated by ExportSqlCe version 3.5.2.18 + ///-- Database information: + ///-- Locale Identifier: 1030 + ///-- Encryption Mode: + ///-- Case Sensitive: False + ///-- Database: C:\Temp\Playground\Umb4110Starterkit\Umb4110Starterkit\App_Data\Umbraco.sdf + ///-- ServerVersion: 4.0.8876.1 + ///-- DatabaseSize: 1114112 + ///-- Created: 10-01-2013 11:39 /// - /// - /// - /// - /// - /// - /// - /// Umbraco database installation script for SQL Server CE 4 + ///-- User Table information: + ///-- Number of tables: 43 + ///-- cmsContent: 12 row(s) + ///-- cmsContentType: 10 row(s) + ///-- cmsContentTypeAllowedContentType: 8 row(s [rest of string was truncated]";. + /// + internal static string SqlCe_SchemaAndData_4110 { + get { + return ResourceManager.GetString("SqlCe_SchemaAndData_4110", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CREATE TABLE [umbracoRelation] + ///( + ///[id] [int] NOT NULL IDENTITY(1, 1), + ///[parentId] [int] NOT NULL, + ///[childId] [int] NOT NULL, + ///[relType] [int] NOT NULL, + ///[datetime] [datetime] NOT NULL CONSTRAINT [DF_umbracoRelation_datetime] DEFAULT (getdate()), + ///[comment] [nvarchar] (1000) NOT NULL + ///) /// - ///IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT - /// - /// Database version: 4.8.0.1 - /// - /// Please increment this version number if ANY change is made to this script, - /// so compatibility with scripts for other database systems can be verified easily. - /// The first 3 digits depict the Umbraco [rest of string was truncated]";. + ///; + ///ALTER TABLE [umbracoRelation] ADD CONSTRAINT [PK_umbracoRelation] PRIMARY KEY ([id]) + ///; + ///CREATE TABLE [cmsDocument] + ///( + ///[nodeId] [int] NOT NULL, + ///[published] [bit] NOT NULL, + ///[documentUser] [int] NOT [rest of string was truncated]";. /// internal static string SqlCeTotal_480 { get { diff --git a/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.resx b/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.resx index 6a41e64087..98029cf667 100644 --- a/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.resx +++ b/src/Umbraco.Tests/Migrations/SqlScripts/SqlResources.resx @@ -124,6 +124,9 @@ sqlcetotal-480.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + sqlce-schemaanddata-4110.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + sqlservertotal-480.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 diff --git a/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs b/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs index 2b15022dd6..beb1d2058d 100644 --- a/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs +++ b/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.ObjectResolution; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Migrations; +using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Tests.TestHelpers; using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; @@ -18,38 +21,38 @@ namespace Umbraco.Tests.Migrations { TestHelper.SetupLog4NetForTests(); - //this ensures its reset - PluginManager.Current = new PluginManager(false); + MigrationResolver.Current = new MigrationResolver(new List + { + typeof (Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero.RemoveUmbracoAppConstraints), + typeof (DeleteAppTables), + typeof (EnsureAppsTreesUpdated), + typeof (MoveMasterContentTypeData), + typeof (NewCmsContentType2ContentTypeTable), + typeof (RemoveMasterContentTypeColumn), + typeof (RenameCmsTabTable), + typeof (RenameTabIdColumn), + typeof (UpdateCmsContentTypeAllowedContentTypeTable), + typeof (UpdateCmsContentTypeTable), + typeof (UpdateCmsContentVersionTable), + typeof (UpdateCmsPropertyTypeGroupTable) + }); - //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver - PluginManager.Current.AssembliesToScan = new[] - { - typeof (MigrationRunner).Assembly - }; + Resolution.Freeze(); SyntaxConfig.SqlSyntaxProvider = SqlCeSyntax.Provider; } - - [Test] - public void Can_Find_Migrations_In_Current_Assembly() - { - var foundTypes = PluginManager.Current.ResolveMigrationTypes(); - - Assert.That(foundTypes.Any(), Is.True); - Assert.That(foundTypes.Count(), Is.GreaterThanOrEqualTo(11)); - } - + [Test] public void Can_Find_Targetted_Migrations() { var configuredVersion = new Version("4.11.0"); var targetVersion = new Version("6.0.0"); - var foundMigrations = PluginManager.Current.FindMigrations(); + var foundMigrations = MigrationResolver.Current.Migrations; var migrationRunner = new MigrationRunner(configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName); var migrations = migrationRunner.OrderedUpgradeMigrations(foundMigrations); - var context = new MigrationContext(DatabaseProviders.SqlServerCE); + var context = new MigrationContext(DatabaseProviders.SqlServerCE, null); foreach (MigrationBase migration in migrations) { migration.GetUpExpressions(context); @@ -66,7 +69,8 @@ namespace Umbraco.Tests.Migrations [TearDown] public void TearDown() { - PluginManager.Current = null; + MigrationResolver.Reset(); + Resolution.IsFrozen = false; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs index 54a8232e68..e229655699 100644 --- a/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs +++ b/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs @@ -1,12 +1,16 @@ using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.ObjectResolution; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Migrations; +using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Strategies.Migrations; using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; namespace Umbraco.Tests.Migrations.Upgrades @@ -15,7 +19,7 @@ namespace Umbraco.Tests.Migrations.Upgrades public abstract class BaseUpgradeTest { /// Regular expression that finds multiline block comments. - private static readonly Regex m_findComments = new Regex(@"\/\*.*?\*\/", RegexOptions.Singleline | RegexOptions.Compiled); + private static readonly Regex FindComments = new Regex(@"\/\*.*?\*\/", RegexOptions.Singleline | RegexOptions.Compiled); [SetUp] public virtual void Initialize() @@ -27,15 +31,24 @@ namespace Umbraco.Tests.Migrations.Upgrades AppDomain.CurrentDomain.SetData("DataDirectory", Path); UmbracoSettings.UseLegacyXmlSchema = false; + + MigrationResolver.Current = new MigrationResolver(new List + { + typeof (Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero.RemoveUmbracoAppConstraints), + typeof (DeleteAppTables), + typeof (EnsureAppsTreesUpdated), + typeof (MoveMasterContentTypeData), + typeof (NewCmsContentType2ContentTypeTable), + typeof (RemoveMasterContentTypeColumn), + typeof (RenameCmsTabTable), + typeof (RenameTabIdColumn), + typeof (UpdateCmsContentTypeAllowedContentTypeTable), + typeof (UpdateCmsContentTypeTable), + typeof (UpdateCmsContentVersionTable), + typeof (UpdateCmsPropertyTypeGroupTable) + }); - //this ensures its reset - PluginManager.Current = new PluginManager(false); - - //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver - PluginManager.Current.AssembliesToScan = new[] - { - typeof (MigrationRunner).Assembly - }; + Resolution.Freeze(); DatabaseSpecificSetUp(); @@ -43,7 +56,7 @@ namespace Umbraco.Tests.Migrations.Upgrades } [Test] - public void Can_Upgrade_From_470_To_600() + public virtual void Can_Upgrade_From_470_To_600() { var configuredVersion = new Version("4.7.0"); var targetVersion = new Version("6.0.0"); @@ -53,13 +66,13 @@ namespace Umbraco.Tests.Migrations.Upgrades //Create db schema and data from old Total.sql file for Sql Ce string statements = GetDatabaseSpecificSqlScript(); // replace block comments by whitespace - statements = m_findComments.Replace(statements, " "); + statements = FindComments.Replace(statements, " "); // execute all non-empty statements foreach (string statement in statements.Split(";".ToCharArray())) { - string rawStatement = statement.Trim(); + string rawStatement = statement.Replace("GO", "").Trim(); if (rawStatement.Length > 0) - db.Execute(rawStatement); + db.Execute(new Sql(rawStatement)); } //Setup the MigrationRunner @@ -82,6 +95,8 @@ namespace Umbraco.Tests.Migrations.Upgrades { PluginManager.Current = null; SyntaxConfig.SqlSyntaxProvider = null; + MigrationResolver.Reset(); + Resolution.IsFrozen = false; TestHelper.CleanContentDirectories(); diff --git a/src/Umbraco.Tests/Migrations/Upgrades/SqlCeDataUpgradeTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/SqlCeDataUpgradeTest.cs new file mode 100644 index 0000000000..a9b80f9b17 --- /dev/null +++ b/src/Umbraco.Tests/Migrations/Upgrades/SqlCeDataUpgradeTest.cs @@ -0,0 +1,73 @@ +using System; +using NUnit.Framework; +using SQLCE4Umbraco; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Migrations; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Web.Strategies.Migrations; +using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; + +namespace Umbraco.Tests.Migrations.Upgrades +{ + [TestFixture, NUnit.Framework.Ignore] + public class SqlCeDataUpgradeTest : BaseUpgradeTest + { + + [Test, NUnit.Framework.Ignore] + public override void Can_Upgrade_From_470_To_600() + { + var configuredVersion = new Version("4.11.0"); + var targetVersion = new Version("6.0.0"); + var provider = GetDatabaseProvider(); + var db = GetConfiguredDatabase(); + + var fix = new PublishAfterUpgradeToVersionSixth(); + + //Setup the MigrationRunner + var migrationRunner = new MigrationRunner(configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName); + bool upgraded = migrationRunner.Execute(db, provider, true); + + Assert.That(upgraded, Is.True); + + bool hasTabTable = db.TableExist("cmsTab"); + bool hasPropertyTypeGroupTable = db.TableExist("cmsPropertyTypeGroup"); + bool hasAppTreeTable = db.TableExist("umbracoAppTree"); + + fix.Unsubscribe(); + + Assert.That(hasTabTable, Is.False); + Assert.That(hasPropertyTypeGroupTable, Is.True); + Assert.That(hasAppTreeTable, Is.False); + } + + public override void DatabaseSpecificSetUp() + { + } + + public override void DatabaseSpecificTearDown() + { + //legacy API database connection close + SqlCeContextGuardian.CloseBackgroundConnection(); + } + + public override ISqlSyntaxProvider GetSyntaxProvider() + { + return SqlCeSyntax.Provider; + } + + public override UmbracoDatabase GetConfiguredDatabase() + { + return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoPetaPocoTests.sdf", "System.Data.SqlServerCe.4.0"); + } + + public override DatabaseProviders GetDatabaseProvider() + { + return DatabaseProviders.SqlServerCE; + } + + public override string GetDatabaseSpecificSqlScript() + { + return SqlScripts.SqlResources.SqlCe_SchemaAndData_4110; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 9c58a27d8b..96a6b3acc8 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Web; using NUnit.Framework; +using Rhino.Mocks; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers.Entities; @@ -39,6 +42,46 @@ namespace Umbraco.Tests.Models Assert.That(content.Properties["title"].Value, Is.EqualTo("This is the new title")); } + [Test] + public void Can_Set_Property_Value_As_String() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); + + // Act + content.SetValue("title", "This is the new title"); + + // Assert + Assert.That(content.Properties.Any(), Is.True); + Assert.That(content.Properties["title"], Is.Not.Null); + Assert.That(content.Properties["title"].Value, Is.EqualTo("This is the new title")); + } + + [Test] + public void Can_Set_Property_Value_As_HttpPostedFileBase() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); + + var stream = new MemoryStream(System.Text.Encoding.Default.GetBytes("TestContent")); + var httpPostedFileBase = MockRepository.GenerateMock(); + httpPostedFileBase.Stub(x => x.ContentLength).Return(Convert.ToInt32(stream.Length)); + httpPostedFileBase.Stub(x => x.ContentType).Return("text/plain"); + httpPostedFileBase.Stub(x => x.FileName).Return("sample.txt"); + httpPostedFileBase.Stub(x => x.InputStream).Return(stream); + + // Assert + content.SetValue("title", httpPostedFileBase); + + // Assert + Assert.That(content.Properties.Any(), Is.True); + Assert.That(content.Properties["title"], Is.Not.Null); + Assert.That(content.Properties["title"].Value, Is.StringContaining("sample.txt")); + } + + [Test] public void Can_Clone_Content() { @@ -159,7 +202,7 @@ namespace Umbraco.Tests.Models HelpText = "", Mandatory = false, SortOrder = 3, - DataTypeId = -88 + DataTypeDefinitionId = -88 }); // Assert @@ -177,7 +220,7 @@ namespace Umbraco.Tests.Models // Act var propertyType = new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { - Alias = "subtitle", Name = "Subtitle", Description = "Optional subtitle", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeId = -88 + Alias = "subtitle", Name = "Subtitle", Description = "Optional subtitle", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 }; contentType.PropertyGroups["Content"].PropertyTypes.Add(propertyType); content.Properties.Add(new Property(propertyType){Value = "This is a subtitle Test"}); @@ -203,7 +246,7 @@ namespace Umbraco.Tests.Models HelpText = "", Mandatory = false, SortOrder = 3, - DataTypeId = -88 + DataTypeDefinitionId = -88 }; var propertyGroup = new PropertyGroup {Name = "Test Group", SortOrder = 3}; propertyGroup.PropertyTypes.Add(propertyType); @@ -228,7 +271,7 @@ namespace Umbraco.Tests.Models // Act - note that the PropertyType's properties like SortOrder is not updated through the Content object var propertyType = new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { - Alias = "title", Name = "Title", Description = "Title description added", HelpText = "", Mandatory = false, SortOrder = 10, DataTypeId = -88 + Alias = "title", Name = "Title", Description = "Title description added", HelpText = "", Mandatory = false, SortOrder = 10, DataTypeDefinitionId = -88 }; content.Properties.Add(new Property(propertyType)); @@ -319,7 +362,7 @@ namespace Umbraco.Tests.Models // Act content.ResetDirtyProperties(); - content.ChangePublishedState(true); + content.ChangePublishedState(PublishedState.Published); // Assert Assert.That(content.IsPropertyDirty("Published"), Is.True); @@ -377,7 +420,7 @@ namespace Umbraco.Tests.Models HelpText = "", Mandatory = false, SortOrder = 3, - DataTypeId = -88 + DataTypeDefinitionId = -88 }; contentType.PropertyGroups["Content"].PropertyTypes.Add(propertyType); @@ -404,7 +447,7 @@ namespace Umbraco.Tests.Models HelpText = "", Mandatory = false, SortOrder = 4, - DataTypeId = -88 + DataTypeDefinitionId = -88 } })); @@ -437,7 +480,7 @@ namespace Umbraco.Tests.Models HelpText = "", Mandatory = false, SortOrder = 4, - DataTypeId = -88 + DataTypeDefinitionId = -88 } })); @@ -472,7 +515,7 @@ namespace Umbraco.Tests.Models HelpText = "", Mandatory = false, SortOrder = 4, - DataTypeId = -88 + DataTypeDefinitionId = -88 } })); var mixin2 = MockedContentTypes.CreateSimpleContentType("mixin2", "Mixin2", new PropertyTypeCollection( @@ -486,7 +529,7 @@ namespace Umbraco.Tests.Models HelpText = "", Mandatory = false, SortOrder = 4, - DataTypeId = -88 + DataTypeDefinitionId = -88 } })); diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs index 343dc0be9b..d080e037b6 100644 --- a/src/Umbraco.Tests/Models/ContentXmlTest.cs +++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs @@ -53,7 +53,7 @@ namespace Umbraco.Tests.Models var content = MockedContent.CreateTextpageContent(contentType, "Root Home", -1); ServiceContext.ContentService.Save(content, 0); - var nodeName = content.ContentType.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); + var nodeName = content.ContentType.Alias.ToSafeAliasWithForcingCheck(); // Act XElement element = content.ToXml(); diff --git a/src/Umbraco.Tests/Models/MacroTests.cs b/src/Umbraco.Tests/Models/MacroTests.cs index 68d3e1b1d5..c45b20f329 100644 --- a/src/Umbraco.Tests/Models/MacroTests.cs +++ b/src/Umbraco.Tests/Models/MacroTests.cs @@ -94,7 +94,7 @@ namespace Umbraco.Tests.Models // Arrange var serviceStackSerializer = new ServiceStackJsonSerializer(); var serializationService = new SerializationService(serviceStackSerializer); - var fileSystem = FileSystemProviderManager.Current.GetFileSystemProvider("macros"); + var fileSystem = new PhysicalFileSystem("~/App_Data/Macros"); var macro = new Macro { @@ -128,7 +128,7 @@ namespace Umbraco.Tests.Models // Arrange var serviceStackSerializer = new ServiceStackJsonSerializer(); var serializationService = new SerializationService(serviceStackSerializer); - var fileSystem = FileSystemProviderManager.Current.GetFileSystemProvider("macros"); + var fileSystem = new PhysicalFileSystem("~/App_Data/Macros"); var macro = new Macro { diff --git a/src/Umbraco.Tests/Persistence/Mappers/PropertyTypeMapperTest.cs b/src/Umbraco.Tests/Persistence/Mappers/PropertyTypeMapperTest.cs index a45cf065e2..f7bfbd71ad 100644 --- a/src/Umbraco.Tests/Persistence/Mappers/PropertyTypeMapperTest.cs +++ b/src/Umbraco.Tests/Persistence/Mappers/PropertyTypeMapperTest.cs @@ -34,13 +34,13 @@ namespace Umbraco.Tests.Persistence.Mappers } [Test] - public void Can_Map_DataTypeId_Property() + public void Can_Map_DataTypeDefinitionId_Property() { // Arrange SyntaxConfig.SqlSyntaxProvider = SqlCeSyntax.Provider; // Act - string column = PropertyTypeMapper.Instance.Map("DataTypeId"); + string column = PropertyTypeMapper.Instance.Map("DataTypeDefinitionId"); // Assert Assert.That(column, Is.EqualTo("[cmsPropertyType].[dataTypeId]")); @@ -66,7 +66,7 @@ namespace Umbraco.Tests.Persistence.Mappers SyntaxConfig.SqlSyntaxProvider = SqlCeSyntax.Provider; // Act - string column = PropertyTypeMapper.Instance.Map("DataTypeControlId"); + string column = PropertyTypeMapper.Instance.Map("DataTypeId"); // Assert Assert.That(column, Is.EqualTo("[cmsDataType].[controlId]")); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index 0f9d10cdc5..bc59b85d18 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -167,8 +167,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); var contentType = contentTypeRepository.Get(1045); - var content = new Content(1048, contentType); - content.Name = "Textpage 2 Child Node"; + var content = new Content("Textpage 2 Child Node", 1048, contentType); content.CreatorId = 0; content.WriterId = 0; diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 0ec1fd5dd0..7fd9ed5127 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Persistence.Repositories HelpText = "", Mandatory = false, SortOrder = 1, - DataTypeId = -88 + DataTypeDefinitionId = -88 }); repository.AddOrUpdate(contentType); unitOfWork.Commit(); @@ -228,6 +228,39 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(contentType.PropertyGroups.Count(), Is.EqualTo(2)); } + [Test] + public void Can_Verify_PropertyType_With_No_Group() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentType = repository.Get(1046); + + // Act + var urlAlias = new PropertyType(new Guid(), DataTypeDatabaseType.Nvarchar) + { + Alias = "urlAlias", + Name = "Url Alias", + Description = "", + HelpText = "", + Mandatory = false, + SortOrder = 1, + DataTypeDefinitionId = -88 + }; + var list = new List {urlAlias}; + ((ContentType) contentType).PropertyTypes = list; + repository.AddOrUpdate(contentType); + unitOfWork.Commit(); + + // Assert + var updated = repository.Get(1046); + Assert.That(updated.PropertyGroups.Count(), Is.EqualTo(2)); + Assert.That(updated.PropertyTypes.Count(), Is.EqualTo(5)); + Assert.That(updated.PropertyTypes.Any(x => x.Alias == "urlAlias"), Is.True); + Assert.AreEqual(updated.PropertyTypes.First(x => x.Alias == "urlAlias").PropertyGroupId, default(int)); + } + [Test] public void Can_Verify_AllowedChildContentTypes_On_ContentType() { @@ -270,6 +303,128 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(updated.AllowedContentTypes.Any(x => x.Alias == simpleSubpageContentType.Alias), Is.True); } + [Test] + public void Can_Verify_Removal_Of_Used_PropertyType_From_ContentType() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentRepository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentType = repository.Get(1046); + + var subpage = MockedContent.CreateTextpageContent(contentType, "Text Page 1", contentType.Id); + contentRepository.AddOrUpdate(subpage); + unitOfWork.Commit(); + + // Act + contentType.RemovePropertyType("keywords"); + repository.AddOrUpdate(contentType); + unitOfWork.Commit(); + + // Assert + Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(3)); + Assert.That(contentType.PropertyTypes.Any(x => x.Alias == "keywords"), Is.False); + Assert.That(subpage.Properties.First(x => x.Alias == "metaDescription").Value, Is.EqualTo("This is the meta description for a textpage")); + } + + [Test] + public void Can_Verify_Addition_Of_PropertyType_After_ContentType_Is_Used() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentRepository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentType = repository.Get(1046); + + var subpage = MockedContent.CreateTextpageContent(contentType, "Text Page 1", contentType.Id); + contentRepository.AddOrUpdate(subpage); + unitOfWork.Commit(); + + // Act + var propertyGroup = contentType.PropertyGroups.First(x => x.Name == "Meta"); + propertyGroup.PropertyTypes.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metaAuthor", Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + repository.AddOrUpdate(contentType); + unitOfWork.Commit(); + + // Assert + Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(5)); + Assert.That(contentType.PropertyTypes.Any(x => x.Alias == "metaAuthor"), Is.True); + } + + [Test] + public void Can_Verify_Usage_Of_New_PropertyType_On_Content() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentRepository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentType = repository.Get(1046); + + var subpage = MockedContent.CreateTextpageContent(contentType, "Text Page 1", contentType.Id); + contentRepository.AddOrUpdate(subpage); + unitOfWork.Commit(); + + var propertyGroup = contentType.PropertyGroups.First(x => x.Name == "Meta"); + propertyGroup.PropertyTypes.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metaAuthor", Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + repository.AddOrUpdate(contentType); + unitOfWork.Commit(); + + // Act + var content = contentRepository.Get(subpage.Id); + content.SetValue("metaAuthor", "John Doe"); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + //Assert + var updated = contentRepository.Get(subpage.Id); + Assert.That(updated.GetValue("metaAuthor").ToString(), Is.EqualTo("John Doe")); + Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(5)); + Assert.That(contentType.PropertyTypes.Any(x => x.Alias == "metaAuthor"), Is.True); + } + + [Test] + public void + Can_Verify_That_A_Combination_Of_Adding_And_Deleting_PropertyTypes_Doesnt_Cause_Issues_For_Content_And_ContentType + () + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentRepository = RepositoryResolver.Current.ResolveByType(unitOfWork); + var contentType = repository.Get(1046); + + var subpage = MockedContent.CreateTextpageContent(contentType, "Text Page 1", contentType.Id); + contentRepository.AddOrUpdate(subpage); + unitOfWork.Commit(); + + //Remove PropertyType + contentType.RemovePropertyType("keywords"); + //Add PropertyType + var propertyGroup = contentType.PropertyGroups.First(x => x.Name == "Meta"); + propertyGroup.PropertyTypes.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metaAuthor", Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + repository.AddOrUpdate(contentType); + unitOfWork.Commit(); + + // Act + var content = contentRepository.Get(subpage.Id); + content.SetValue("metaAuthor", "John Doe"); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + //Assert + var updated = contentRepository.Get(subpage.Id); + Assert.That(updated.GetValue("metaAuthor").ToString(), Is.EqualTo("John Doe")); + Assert.That(updated.Properties.First(x => x.Alias == "metaDescription").Value, Is.EqualTo("This is the meta description for a textpage")); + + Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(4)); + Assert.That(contentType.PropertyTypes.Any(x => x.Alias == "metaAuthor"), Is.True); + Assert.That(contentType.PropertyTypes.Any(x => x.Alias == "keywords"), Is.False); + } + public void CreateTestData() { //Create and Save ContentType "umbTextpage" -> 1045 diff --git a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs index e7f48fa4d3..fe841d804b 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs @@ -160,6 +160,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(exists, Is.True); } + [Ignore] [Test] public void Can_Perform_Update_On_DictionaryRepository() { diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs index a39dba0986..b7315de594 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -77,7 +77,7 @@ namespace Umbraco.Tests.Persistence.Repositories HelpText = "", Mandatory = false, SortOrder = 1, - DataTypeId = -88 + DataTypeDefinitionId = -88 }); repository.AddOrUpdate(mediaType); unitOfWork.Commit(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs index 3069196cd9..c594061b4e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.Persistence.Repositories [SetUp] public void Initialize() { - _fileSystem = FileSystemProviderManager.Current.GetFileSystemProvider("scripts"); + _fileSystem = new PhysicalFileSystem(SystemDirectories.Scripts); var stream = CreateStream("Umbraco.Sys.registerNamespace(\"Umbraco.Utils\");"); _fileSystem.AddFile("test-script.js", stream); } @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Persistence.Repositories var unitOfWork = provider.GetUnitOfWork(); // Act - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); // Assert Assert.That(repository, Is.Not.Null); @@ -42,7 +42,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); // Act var script = new Script("test-add-script.js") {Content = "/// "}; @@ -59,7 +59,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); // Act var script = new Script("test-updated-script.js") { Content = "/// " }; @@ -83,7 +83,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); // Act var script = repository.Get("test-script.js"); @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); // Act var exists = repository.Get("test-script.js"); @@ -117,7 +117,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); var script = new Script("test-script1.js") { Content = "/// " }; repository.AddOrUpdate(script); @@ -143,7 +143,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); var script = new Script("test-script1.js") { Content = "/// " }; repository.AddOrUpdate(script); @@ -169,7 +169,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new ScriptRepository(unitOfWork); + var repository = new ScriptRepository(unitOfWork, _fileSystem); // Act var exists = repository.Exists("test-script.js"); @@ -183,7 +183,7 @@ namespace Umbraco.Tests.Persistence.Repositories { _fileSystem = null; //Delete all files - var fs = FileSystemProviderManager.Current.GetFileSystemProvider("scripts"); + var fs = new PhysicalFileSystem(SystemDirectories.Scripts); var files = fs.GetFiles("", "*.js"); foreach (var file in files) { diff --git a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs index 0b18cb253f..76930a98ba 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.Persistence.Repositories [SetUp] public void Initialize() { - _fileSystem = FileSystemProviderManager.Current.GetFileSystemProvider("stylesheets"); + _fileSystem = new PhysicalFileSystem(SystemDirectories.Css); var stream = CreateStream("body {background:#EE7600; color:#FFF;}"); _fileSystem.AddFile("styles.css", stream); } @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Persistence.Repositories var unitOfWork = provider.GetUnitOfWork(); // Act - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); // Assert Assert.That(repository, Is.Not.Null); @@ -42,7 +42,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); // Act var stylesheet = new Stylesheet("test-add.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; @@ -59,7 +59,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); // Act var stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); // Act var stylesheet = new Stylesheet("test-delete.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; @@ -105,7 +105,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); // Act var stylesheet = repository.Get("styles.css"); @@ -123,7 +123,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); var stylesheet = new Stylesheet("styles-v2.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.AddOrUpdate(stylesheet); @@ -145,7 +145,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); var stylesheet = new Stylesheet("styles-v2.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.AddOrUpdate(stylesheet); @@ -167,7 +167,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange var provider = new FileUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); - var repository = new StylesheetRepository(unitOfWork); + var repository = new StylesheetRepository(unitOfWork, _fileSystem); // Act var exists = repository.Exists("styles.css"); diff --git a/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs index b1b66ad110..591510d1ce 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs @@ -199,6 +199,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(4444, result.Id); } + [Ignore] [Test] public void Complex_Linq() { @@ -228,6 +229,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(3, doc.Index()); } + [Ignore] [Test] public void Is_First() { @@ -241,6 +243,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsFalse(doc.IsFirst()); } + [Ignore] [Test] public void Is_Not_First() { @@ -254,6 +257,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsTrue(doc.IsNotFirst()); } + [Ignore] [Test] public void Is_Position() { @@ -442,6 +446,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual("Custom data with same property name as the member name", asDynamic.GetPropertyValue("CreatorName")); } + [Ignore] [Test] public void GetPropertyValue_Reflected() { @@ -462,6 +467,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual("admin", asDynamic.CreatorName); } + [Ignore] [Test] public void Get_Member_Property() { diff --git a/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs index 7c1a3598fb..4fc304730a 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs @@ -1,12 +1,69 @@ using System; using System.Diagnostics; +using System.Xml; +using System.Xml.Linq; using Microsoft.CSharp.RuntimeBinder; using NUnit.Framework; using Umbraco.Core.Dynamics; using System.Linq; +using Umbraco.Tests.PartialTrust; +using Umbraco.Core; namespace Umbraco.Tests.PublishedContent { + [TestFixture] + public class DynamicXmlConverterTests : AbstractPartialTrustFixture + { + [Test] + public void Convert_To_String() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result); + } + + [Test] + public void Convert_To_XElement() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.ToString(SaveOptions.DisableFormatting)); + } + + [Test] + public void Convert_To_XmlElement() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.OuterXml); + } + + [Test] + public void Convert_To_XmlDocument() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.InnerXml); + } + + public override void TestSetup() + { + + } + + public override void TestTearDown() + { + } + } + [TestFixture] public class DynamicXmlTests { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 8a43876960..855a23c4c3 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -206,6 +206,7 @@ namespace Umbraco.Tests.PublishedContent } } + [Ignore] [Test] public void Children_Without_Examine() { @@ -229,7 +230,8 @@ namespace Umbraco.Tests.PublishedContent var subChildren = publishedChild1.Children(); Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); } - + + [Ignore] [Test] public void Descendants_Without_Examine() { @@ -254,6 +256,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); } + [Ignore] [Test] public void DescendantsOrSelf_Without_Examine() { @@ -280,6 +283,7 @@ namespace Umbraco.Tests.PublishedContent new[] { mChild1.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); } + [Ignore] [Test] public void Parent_Without_Examine() { @@ -305,6 +309,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent.Id); } + [Ignore] [Test] public void Ancestors_Without_Examine() { @@ -324,6 +329,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsTrue(publishedSubChild1.Ancestors().Select(x => x.Id).ContainsAll(new[] {mChild1.Id, mRoot.Id})); } + [Ignore] [Test] public void AncestorsOrSelf_Without_Examine() { diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index e35297aa3d..ef3e76bc51 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -32,7 +32,8 @@ namespace Umbraco.Tests.Routing /// /// This checks that when we retreive a NiceUrl for multiple items that there are no issues with cache overlap /// and that they are all cached correctly. - /// + /// + [Ignore] [Test] public void Ensure_Cache_Is_Correct() { diff --git a/src/Umbraco.Tests/Services/BaseServiceTest.cs b/src/Umbraco.Tests/Services/BaseServiceTest.cs index ad8cf22596..6fb9334fd7 100644 --- a/src/Umbraco.Tests/Services/BaseServiceTest.cs +++ b/src/Umbraco.Tests/Services/BaseServiceTest.cs @@ -61,7 +61,7 @@ namespace Umbraco.Tests.Services //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1047 Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id); subpage.ReleaseDate = DateTime.Now.AddMinutes(-5); - subpage.ChangePublishedState(false); + subpage.ChangePublishedState(PublishedState.Saved); ServiceContext.ContentService.Save(subpage, 0); //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1048 diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 8c6ab28f56..235c2ede83 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -47,7 +47,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; // Act - var content = contentService.CreateContent(-1, "umbTextpage", 0); + var content = contentService.CreateContent("Test", -1, "umbTextpage", 0); // Assert Assert.That(content, Is.Not.Null); @@ -87,7 +87,7 @@ namespace Umbraco.Tests.Services contentService.SetHttpContext(base.GetUmbracoContext("/test", 1234).HttpContext); // Act - var content = contentService.CreateContent(-1, "umbTextpage"); + var content = contentService.CreateContent("Test", -1, "umbTextpage"); // Assert Assert.That(content, Is.Not.Null); @@ -127,7 +127,7 @@ namespace Umbraco.Tests.Services // Act var contentService = ServiceContext.ContentService as ContentService; contentService.SetHttpContext(null); - var content = contentService.CreateContent(-1, "umbTextpage"); + var content = contentService.CreateContent("Test", -1, "umbTextpage"); // Assert Assert.That(content, Is.Not.Null); @@ -142,7 +142,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; // Act & Assert - Assert.Throws(() => contentService.CreateContent(-1, "umbAliasDoesntExist")); + Assert.Throws(() => contentService.CreateContent("Test", -1, "umbAliasDoesntExist")); } [Test] @@ -203,6 +203,23 @@ namespace Umbraco.Tests.Services Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(2)); } + [Test] + public void Can_Get_Descendents_Of_Contnet() + { + // Arrange + var contentService = ServiceContext.ContentService; + var hierarchy = CreateContentHierarchy(); + contentService.Save(hierarchy, 0); + + // Act + var contents = contentService.GetDescendants(1046); + + // Assert + Assert.That(contents, Is.Not.Null); + Assert.That(contents.Any(), Is.True); + Assert.That(contents.Count(), Is.EqualTo(52)); + } + [Test] public void Can_Get_All_Versions_Of_Content() { @@ -466,8 +483,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent(1046, "umbTextpage", 0); - content.Name = "Subpage with Unpublisehed Parent"; + var content = contentService.CreateContent("Subpage with Unpublisehed Parent", 1046, "umbTextpage", 0); contentService.Save(content, 0); // Act @@ -499,8 +515,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent(-1, "umbTextpage", 0); - content.Name = "Home US"; + var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0); content.SetValue("author", "Barack Obama"); // Act @@ -517,8 +532,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent(-1, "umbTextpage", 0); - content.Name = "Home US"; + var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0); content.SetValue("author", "Barack Obama"); // Act diff --git a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs index f137e98cba..c64e1b9628 100644 --- a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs @@ -82,16 +82,17 @@ namespace Umbraco.Tests.Services Debug.WriteLine("Created content on thread: " + Thread.CurrentThread.ManagedThreadId); //create 2 content items + + string name1 = "test" + Guid.NewGuid(); + var content1 = contentService.CreateContent(name1, -1, "umbTextpage", 0); - var content1 = contentService.CreateContent(-1, "umbTextpage", 0); - content1.Name = "test" + Guid.NewGuid(); Debug.WriteLine("Saving content1 on thread: " + Thread.CurrentThread.ManagedThreadId); contentService.Save(content1); Thread.Sleep(100); //quick pause for maximum overlap! - var content2 = contentService.CreateContent(-1, "umbTextpage", 0); - content2.Name = "test" + Guid.NewGuid(); + string name2 = "test" + Guid.NewGuid(); + var content2 = contentService.CreateContent(name2, -1, "umbTextpage", 0); Debug.WriteLine("Saving content2 on thread: " + Thread.CurrentThread.ManagedThreadId); contentService.Save(content2); } @@ -145,15 +146,15 @@ namespace Umbraco.Tests.Services //create 2 content items - var folder1 = mediaService.CreateMedia(-1, "Folder", 0); - folder1.Name = "test" + Guid.NewGuid(); + string name1 = "test" + Guid.NewGuid(); + var folder1 = mediaService.CreateMedia(name1, -1, "Folder", 0); Debug.WriteLine("Saving folder1 on thread: " + Thread.CurrentThread.ManagedThreadId); mediaService.Save(folder1, 0); Thread.Sleep(100); //quick pause for maximum overlap! - var folder2 = mediaService.CreateMedia(-1, "Folder", 0); - folder2.Name = "test" + Guid.NewGuid(); + string name = "test" + Guid.NewGuid(); + var folder2 = mediaService.CreateMedia(name, -1, "Folder", 0); Debug.WriteLine("Saving folder2 on thread: " + Thread.CurrentThread.ManagedThreadId); mediaService.Save(folder2, 0); } diff --git a/src/Umbraco.Tests/StringExtensionsTests.cs b/src/Umbraco.Tests/StringExtensionsTests.cs index 49f8f830d7..c00b3cebac 100644 --- a/src/Umbraco.Tests/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/StringExtensionsTests.cs @@ -10,6 +10,16 @@ namespace Umbraco.Tests [TestFixture] public class StringExtensionsTests { + + [TestCase("This is a string to encrypt")] + public void Encrypt_And_Decrypt(string input) + { + var encrypted = input.EncryptWithMachineKey(); + var decrypted = encrypted.DecryptWithMachineKey(); + Assert.AreNotEqual(input, encrypted); + Assert.AreEqual(input, decrypted); + } + [TestCase("Hello this is my string", " string", "Hello this is my")] [TestCase("Hello this is my string strung", " string", "Hello this is my string strung")] [TestCase("Hello this is my string string", " string", "Hello this is my")] diff --git a/src/Umbraco.Tests/TeamCity.proj b/src/Umbraco.Tests/TeamCity.proj new file mode 100644 index 0000000000..0bc83a35f4 --- /dev/null +++ b/src/Umbraco.Tests/TeamCity.proj @@ -0,0 +1,19 @@ + + + + ..\..\tools\MSBuildCommunityTasks + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index c980f10bc5..b2303f7cd9 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -8,7 +8,7 @@ namespace Umbraco.Tests.TestHelpers.Entities { public static Content CreateSimpleContent(IContentType contentType) { - var content = new Content(-1, contentType) { Name = "Home", Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; + var content = new Content("Home", -1, contentType) { Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; object obj = new { @@ -24,7 +24,7 @@ namespace Umbraco.Tests.TestHelpers.Entities public static Content CreateSimpleContent(IContentType contentType, string name, int parentId) { - var content = new Content(parentId, contentType) { Name = name, Language = "en-US", CreatorId = 0, WriterId = 0 }; + var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 }; object obj = new { @@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Entities public static Content CreateSimpleContent(IContentType contentType, string name, IContent parent) { - var content = new Content(parent, contentType) { Name = name, Language = "en-US", CreatorId = 0, WriterId = 0 }; + var content = new Content(name, parent, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 }; object obj = new { @@ -56,7 +56,7 @@ namespace Umbraco.Tests.TestHelpers.Entities public static Content CreateTextpageContent(IContentType contentType, string name, int parentId) { - var content = new Content(parentId, contentType) { Name = name, Language = "en-US", CreatorId = 0, WriterId = 0}; + var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0}; object obj = new { @@ -78,7 +78,7 @@ namespace Umbraco.Tests.TestHelpers.Entities for (int i = 0; i < amount; i++) { var name = "Textpage No-" + i; - var content = new Content(parentId, contentType) { Name = name, Language = "en-US", CreatorId = 0, WriterId = 0 }; + var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 }; object obj = new { diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 6b1544b88b..b3a7f257c9 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -21,12 +21,12 @@ namespace Umbraco.Tests.TestHelpers.Entities }; var contentCollection = new PropertyTypeCollection(); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 }); var metaCollection = new PropertyTypeCollection(); - metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "keywords", Name = "Meta Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metaDescription", Name = "Meta Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -89 }); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "keywords", Name = "Meta Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metaDescription", Name = "Meta Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -89 }); contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Meta", SortOrder = 2 }); @@ -49,8 +49,8 @@ namespace Umbraco.Tests.TestHelpers.Entities }; var metaCollection = new PropertyTypeCollection(); - metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metakeywords", Name = "Meta Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metadescription", Name = "Meta Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -89 }); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metakeywords", Name = "Meta Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metadescription", Name = "Meta Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -89 }); contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Meta", SortOrder = 2 }); @@ -72,9 +72,9 @@ namespace Umbraco.Tests.TestHelpers.Entities }; var contentCollection = new PropertyTypeCollection(); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 }); contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); @@ -95,9 +95,9 @@ namespace Umbraco.Tests.TestHelpers.Entities contentType.Trashed = false; var contentCollection = new PropertyTypeCollection(); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 }); contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); @@ -119,9 +119,9 @@ namespace Umbraco.Tests.TestHelpers.Entities }; var contentCollection = new PropertyTypeCollection(); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = mandatory, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = mandatory, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = mandatory, SortOrder = 3, DataTypeId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = mandatory, SortOrder = 1, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = mandatory, SortOrder = 2, DataTypeDefinitionId = -87 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = mandatory, SortOrder = 3, DataTypeDefinitionId = -88 }); contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); @@ -162,8 +162,8 @@ namespace Umbraco.Tests.TestHelpers.Entities }; var contentCollection = new PropertyTypeCollection(); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Nvarchar) { Alias = "videoFile", Name = "Video File", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Nvarchar) { Alias = "videoFile", Name = "Video File", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 }); mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs index f6844477ff..652ca4bca2 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs @@ -1,5 +1,4 @@ using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; namespace Umbraco.Tests.TestHelpers.Entities { @@ -7,9 +6,8 @@ namespace Umbraco.Tests.TestHelpers.Entities { public static IMedia CreateMediaImage(IMediaType mediaType, int parentId) { - var media = new Media(parentId, mediaType) + var media = new Media("Test Image", parentId, mediaType) { - Name = "Test Image", CreatorId = 0 }; @@ -24,9 +22,8 @@ namespace Umbraco.Tests.TestHelpers.Entities public static IMedia CreateMediaFile(IMediaType mediaType, int parentId) { - var media = new Media(parentId, mediaType) + var media = new Media("Test File", parentId, mediaType) { - Name = "Test File", CreatorId = 0 }; @@ -39,9 +36,8 @@ namespace Umbraco.Tests.TestHelpers.Entities public static IMedia CreateMediaFolder(IMediaType mediaType, int parentId) { - var media = new Media(parentId, mediaType) + var media = new Media("Test Folder", parentId, mediaType) { - Name = "Test Folder", CreatorId = 0 }; diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 20e23f2084..819ccdb68b 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -23,7 +23,7 @@ namespace Umbraco.Tests.TestHelpers public static void ClearDatabase() { var databaseSettings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; - var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString) as SqlCEHelper; + var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false) as SqlCEHelper; if (dataHelper == null) throw new InvalidOperationException("The sql helper for unit tests must be of type SqlCEHelper, check the ensure the connection string used for this test is set to use SQLCE"); @@ -41,7 +41,7 @@ namespace Umbraco.Tests.TestHelpers ClearDatabase(); var databaseSettings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; - var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString); + var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false); var installer = dataHelper.Utility.CreateInstaller(); if (installer.CanConnect) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 54c151d146..d40b0d1fef 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -164,11 +164,11 @@ SqlResources.resx - + @@ -410,6 +410,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c71a8b1c14..d35d2fde3b 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -117,17 +117,21 @@ ..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll + + False + ..\packages\MySQL.Data.5.1.2.2\lib\MySql.Data.dll + False - ..\packages\uGoLive.1.0.0\lib\Our.Umbraco.uGoLive.dll + ..\packages\uGoLive.1.3.0\lib\Our.Umbraco.uGoLive.dll False - ..\packages\uGoLive.1.0.0\lib\Our.Umbraco.uGoLive.47x.dll + ..\packages\uGoLive.1.3.0\lib\Our.Umbraco.uGoLive.47x.dll False - ..\packages\uGoLive.1.0.0\lib\Our.Umbraco.uGoLive.Checks.dll + ..\packages\uGoLive.1.3.0\lib\Our.Umbraco.uGoLive.Checks.dll System @@ -1014,6 +1018,8 @@ + + @@ -1024,10 +1030,14 @@ + + + + @@ -1824,14 +1834,31 @@ applications.config Designer - - - - - - - - + + Code + + + + + + + + + + + + Code + + + + + + + + + + + @@ -1848,14 +1875,8 @@ - - - - - - diff --git a/src/Umbraco.Web.UI/config/ClientDependency.config b/src/Umbraco.Web.UI/config/ClientDependency.config index d6edcff895..91d1e0bb24 100644 --- a/src/Umbraco.Web.UI/config/ClientDependency.config +++ b/src/Umbraco.Web.UI/config/ClientDependency.config @@ -10,7 +10,7 @@ NOTES: * Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config * A new version will invalidate both client and server cache and create new persisted files --> - + diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index 7588d58311..e4c09f3efd 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -60,7 +60,11 @@ /umbraco/dashboard/startupdashboardkits.ascx - editorwriter + + editor + writer + + /umbraco/dashboard/startupdashboardvideos.ascx @@ -88,14 +92,6 @@ -
- - courier - - - /umbraco/plugins/courier/dashboard/CourierDashboard.ascx - -
developer @@ -104,12 +100,4 @@ /umbraco/plugins/uGoLive/Dashboard.ascx
-
- - content - - - /usercontrols/blog/CommentModeration.ascx - -
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/FileSystemProviders.Release.config b/src/Umbraco.Web.UI/config/FileSystemProviders.Release.config index de62e423b1..fb701736c0 100644 --- a/src/Umbraco.Web.UI/config/FileSystemProviders.Release.config +++ b/src/Umbraco.Web.UI/config/FileSystemProviders.Release.config @@ -7,33 +7,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/Umbraco.Web.UI/config/FileSystemProviders.config b/src/Umbraco.Web.UI/config/FileSystemProviders.config index 527a073ac4..457259fae9 100644 --- a/src/Umbraco.Web.UI/config/FileSystemProviders.config +++ b/src/Umbraco.Web.UI/config/FileSystemProviders.config @@ -7,38 +7,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web.UI/config/UrlRewriting.config b/src/Umbraco.Web.UI/config/UrlRewriting.config index 05ffe1eeb6..20e84a9dcc 100644 --- a/src/Umbraco.Web.UI/config/UrlRewriting.config +++ b/src/Umbraco.Web.UI/config/UrlRewriting.config @@ -28,5 +28,6 @@ Any bugs or problems with the rewriter, contact Anders/Duckie --> + - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/splashes/booting.aspx b/src/Umbraco.Web.UI/config/splashes/booting.aspx index 39e97e4ffe..267e36b369 100644 --- a/src/Umbraco.Web.UI/config/splashes/booting.aspx +++ b/src/Umbraco.Web.UI/config/splashes/booting.aspx @@ -1,15 +1,23 @@ <%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %> +<% + // NH: Adds this inline check to avoid a simple codebehind file in the legacy project! + if (!umbraco.cms.helpers.url.ValidateProxyUrl(Request["url"], Request.Url.AbsoluteUri)) + { + throw new ArgumentException("Can't redirect to the requested url - it's not local or an approved proxy url", + "url"); + } +%> - + The website is restarting - "> + ">

The website is restarting

Please wait for 10s while we prepare to serve the page you have requested...

- +

You can modify the design of this page by editing /config/splashes/booting.aspx

diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index d213e0909e..d70b455d11 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -5,7 +5,7 @@ jpeg,jpg,gif,bmp,png,tiff,tif - alt,border,class,style,align,id,name,onclick,usemap + src,alt,border,class,style,align,id,name,onclick,usemap diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 79d8dd0ea9..372777126a 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -5,7 +5,7 @@ jpeg,jpg,gif,bmp,png,tiff,tif - alt,border,class,style,align,id,name,onclick,usemap + src,alt,border,class,style,align,id,name,onclick,usemap diff --git a/src/Umbraco.Web.UI/config/xsltExtensions.config b/src/Umbraco.Web.UI/config/xsltExtensions.config index 58a11e1d5f..354f37fd64 100644 --- a/src/Umbraco.Web.UI/config/xsltExtensions.config +++ b/src/Umbraco.Web.UI/config/xsltExtensions.config @@ -1,4 +1,4 @@ - + @@ -8,4 +8,8 @@ - + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 4a5e3f8da5..ee42677b44 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -9,7 +9,8 @@ + - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml index c9b51953be..b03071a85d 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml @@ -134,7 +134,9 @@
Medie type
+ /create/simple.ascx +
diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml index fd6c68fd4d..fcd9e09e51 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml @@ -134,7 +134,9 @@
Medie type
+ /create/simple.ascx +
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index cbdb999bb6..6edddefe70 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1,4 +1,4 @@ - + umbraco @@ -37,7 +37,7 @@ Add new Domain - Invalid hostname + Invalid hostname Domain New domain '%0%' has been created Domain '%0%' is deleted @@ -103,6 +103,7 @@ Page Title Properties This document is published but is not visible because the parent '%0%' is unpublished + Oops: this document is published but is not in the cache (internal error) Publish Publication Status Publish at @@ -781,10 +782,10 @@ To manage your website, simply open the umbraco back office and start adding con Xslt saved No errors in xslt Content unpublished - Partial view saved - Partial view saved without any errors! - Partial view not saved - An error occurred saving the file. + Partial view saved + Partial view saved without any errors! + Partial view not saved + An error occurred saving the file. Uses CSS syntax ex: h1, .redHeader, .blueTex @@ -963,4 +964,4 @@ To manage your website, simply open the umbraco back office and start adding con User types Writer - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index dc593fec55..a0dec8b6a4 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1,4 +1,4 @@ - + umbraco @@ -36,7 +36,7 @@ Add new Domain - Invalid hostname + Invalid hostname Domain New domain '%0%' has been created Domain '%0%' is deleted @@ -102,6 +102,7 @@ Page Title Properties This document is published but is not visible because the parent '%0%' is unpublished + Oops: this document is published but is not in the cache (internal error) Publish Publication Status Publish at @@ -779,10 +780,10 @@ 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 - Partial view saved - Partial view saved without any errors! - Partial view not saved - An error occurred saving the file. + Partial view saved + Partial view saved without any errors! + Partial view not saved + An error occurred saving the file. Uses CSS syntax ex: h1, .redHeader, .blueTex @@ -961,4 +962,4 @@ To manage your website, simply open the umbraco back office and start adding con User types Writer - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml index d7fd5b3e18..65ad6abb02 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml @@ -32,6 +32,7 @@ Ordina Invia la pubblicazione Traduci + Annulla pubblicazione Aggiorna @@ -40,8 +41,9 @@ - per esempio: yourdomain.com, www.yourdomain.com + + Hostname non valido Modifica il dominio corrente @@ -69,6 +71,7 @@ Salva e pubblica Salva e invia per approvazione Anteprima + Scegli lo stile Mostra gli stili Inserisci tabella @@ -553,6 +556,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i + @@ -600,6 +604,9 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i Utenti + + Tipo di contenuto master abilitato + Questo tipo di contenuto usa @@ -627,6 +634,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i Tab creata Tab eliminata Tab con id: %0% eliminata + Contenuto non pubblicato @@ -651,6 +659,10 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i + Partial view salvata + Partial view salvata senza errori! + Partial view non salvata + Errore durante il salvataggio del file. @@ -802,6 +814,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i Campo Categoria Cambia la tua password + Conferma la nuova password Contenuto del canale Ridireziona al canvas al login Campo Descrizione @@ -813,6 +826,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i Login Sezioni + Modifica la tua password Password @@ -834,4 +848,4 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i Writer - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index b4892f3e89..7b5daa8c96 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -1,4 +1,4 @@ - + Alexander Bryukhov (Unico Design company) @@ -32,6 +32,7 @@ Сортировать Направить на публикацию Перевести + Скрыть Обновить @@ -40,8 +41,13 @@ Создан новый домен '%0%' Домен '%0%' удален Домен с именем '%0%' уже существует - Например: yourdomain.com, www.yourdomain.com + + + https://www.example.com/, example.com/en, и т.п. Используйте символ '*'
+ если домен не важен, а нужно просто установить язык.]]> +
Домен '%0%' обновлен + Неверное имя домена Править существующие домены @@ -69,6 +75,7 @@ Сохранить и опубликовать Сохранить и направить на публикацию Предварительный просмотр + Предварительный просмотр запрещен, так как документу не сопоставлен шаблон Показать стили Выбрать стиль Вставить таблицу @@ -265,6 +272,7 @@ Email адрес Ошибка Найти + Папка Высота Справка Иконка @@ -591,6 +599,11 @@ + + + Включая неопубликованные дочерние документы Идет публикация. Пожалуйста, подождите... %0% из %1% документов опубликованы... @@ -640,6 +653,9 @@ Пользователи + Родительский тип контента разрешен + Данный тип контента использует + в качестве родительского типа. Вкладки родительского типа не показаны и могут быть изменены непосредственно в родительском типе Шаблон по-умолчанию Словарная статья Чтобы импортировать тип документа, найдите файл ".udt" на своем компьютере, нажав на кнопку "Обзор", затем нажмите "Импортировать" (на следующем экране будет запрошено подтверждение для этой операции). @@ -667,6 +683,7 @@ Вкладка создана Вкладка удалена Вкладка с идентификатором (id): %0% удалена + Документ скрыт (публикация отменена) Стиль CSS не сохранен Стиль CSS сохранен Стиль CSS сохранен без ошибок @@ -691,6 +708,10 @@ Файл сохранен Файл сохранен без ошибок Язык сохранен + Представление не сохранено + Произошла ошибка при сохранении файла + Представление сохранено + Представление сохранено без ошибок Cкрипт Python не сохранен Cкрипт Python не может быть сохранен в связи с ошибками Cкрипт Python сохранен @@ -842,6 +863,7 @@ Поле категории Изменить пароль Вы можете сменить свой пароль для доступа к административной панели Umbraco, заполнив нижеследующие поля и нажав на кнопку 'Изменить пароль' + Подтверждение нового пароля Канал содержимого При входе перевести в режим редактирования "на месте" Поле описания @@ -853,6 +875,7 @@ Имя входа (логин) Начальный узел в Медиа-библиотеке Разделы + Новый пароль Отключить доступ к административной панели Umbraco Пароль Ваш пароль доступа изменен! @@ -875,4 +898,4 @@ Типы пользователей Автор -
+ \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml index ec2b83242b..189c352d63 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml @@ -32,6 +32,7 @@ 排序 提交至发布者 翻译 + 取消发布 更新 @@ -40,8 +41,13 @@ 新域名 '%0%' 已创建 域名 '%0%' 已删除 域名 '%0%' 已使用 - 例如:yourdomain.com、www.yourdomain.com + + + https://www.example.com/、example.com/en、……使用 * 代表任意域名,
+ 只需要设置语言部分即可。]]> +
域名 '%0%' 已更新 + 域名错误 编辑当前域名 @@ -69,6 +75,7 @@ 保存并发布 保存并提交审核 预览 + 因未设置模板无法预览 选择样式 显示样式 插入表格 @@ -580,6 +587,11 @@ + + + 包含未发布的子项 正在发布,请稍候… %0% 中的 %1% 页面已发布… @@ -629,6 +641,9 @@ 用户 + 作为主控文档类型. 主控文档类型的标签只能在主控文档类型里修改。 + 主控文档类型激活 + 该文档类型使用 默认模板 字典键 要导入文档类型,请点击“浏览”按钮,再点击“导入”,然后在您电脑上查找 ".udt"文件导入(下一页中需要您再次确认) @@ -656,6 +671,7 @@ 选项卡已创建 选项卡已删除 id为%0%的选项卡已删除 + 内容已取消发布 样式表未保存 样式表已保存 样式表保存,无错误。 @@ -693,6 +709,10 @@ XSLT无法保存,请检查权限。 XSLT已保存 XSLT无错误 + Partial视图已保存 + Partial视图保存,无错误。 + Partial视图未保存 + Partial视图因为错误未能保存 使用CSS语法,如:h1、.redHeader、.blueTex。 @@ -828,6 +848,7 @@ 分类字段 更改密码 要改变密码,请在框中输入新密码,然后单击“更改密码”。 + 确认新密码 内容频道 登录后进入实时编辑模式 描述字段 @@ -839,6 +860,7 @@ 登录 默认打开媒体项 区域 + 更改密码 禁用后台管理界面 密码 您的密码已更改! @@ -860,4 +882,4 @@ 用户类型 撰稿人 - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx index d0d0899bd7..a2f8735faa 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx +++ b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx @@ -76,8 +76,16 @@ + + + +
+ Only Content Types with this checked can be created at the root level of Content and Media trees +
+
+ @@ -85,6 +93,7 @@
+

Master Content Type enabled
This Content Type uses as a Master Content Type. Properties from Master Content Types are not shown and can only be edited on the Master Content Type itself

diff --git a/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs b/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs index 39b7e7d4a5..8cc70f83ba 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs +++ b/src/Umbraco.Web.UI/umbraco/developer/Macros/EditMacro.aspx.cs @@ -33,8 +33,8 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros { base.PopulateFieldsOnLoad(macro, macroAssemblyValue, macroTypeValue); //check if the ScriptingFile property contains the MacroPartials path - if (!macro.ScriptingFile.IsNullOrWhiteSpace() - && (macro.ScriptingFile.StartsWith(SystemDirectories.MvcViews + "/MacroPartials/") + if (macro.ScriptingFile.IsNullOrWhiteSpace() == false && + (macro.ScriptingFile.StartsWith(SystemDirectories.MvcViews + "/MacroPartials/") || (Regex.IsMatch(macro.ScriptingFile, "~/App_Plugins/.+?/Views/MacroPartials", RegexOptions.Compiled)))) { macroPython.Text = ""; diff --git a/src/Umbraco.Web.UI/umbraco/developer/Python/editPython.aspx b/src/Umbraco.Web.UI/umbraco/developer/Python/editPython.aspx index b86f1226b1..f1c3b6d617 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Python/editPython.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Python/editPython.aspx @@ -1,7 +1,12 @@ <%@ Page ValidateRequest="false" Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" - CodeBehind="editPython.aspx.cs" AutoEventWireup="True" Inherits="umbraco.cms.presentation.developer.editPython" %> + CodeBehind="editPython.aspx.cs" AutoEventWireup="True" Inherits="umbraco.cms.presentation.developer.editPython" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> + + + + + +
diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Breadcrumb.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Breadcrumb.cshtml index 48f6d3ed20..97d1fda58f 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Breadcrumb.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Breadcrumb.cshtml @@ -1,21 +1,24 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + @* -BREADCRUMB -================================= This snippet makes a breadcrumb of parents using an unordred html list. How it works: - It uses the Ancestors() method to get all parents and then generates links so the visitor get go back - Finally it outputs the name of the current page (without a link) - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! *@ -@inherits umbraco.MacroEngines.DynamicNodeContext -
    - @foreach(var level in @Model.Ancestors().Where("Visible")) + +@if (Model.Ancestors().Any()) { -
  • @level.Name
  • + - +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/EmptyTemplate.cshtml new file mode 100644 index 0000000000..b767d94901 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/EmptyTemplate.cshtml @@ -0,0 +1,17 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + +@* + Model = The current page the macro is executed on + @Model.bodyText + + Parameter = collection of parameter values passed from the macro + @Paramter.myParam + + Library = utillity library with common methods + @Library.NodeById(1233) +*@ + + + +@* The fun starts here *@ + diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Gallery.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Gallery.cshtml new file mode 100644 index 0000000000..0db5339928 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Gallery.cshtml @@ -0,0 +1,32 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + +@* +Macro to display a gallery from a media folder. Add the below parameter to the macro +and use it to point the macro at a specific media folder to display it's content as +a simple list. + +Macro Parameters To Create, for this macro to work: +Show:True Alias:mediaId Name:Media Folder ID Type:MediaCurrent +*@ + + +@if (Parameter.mediaId != null) +{ + @* Get the media folder as a dynamic node *@ + var mediaFolder = Library.MediaById(Parameter.mediaId); + + if (mediaFolder.Children.Any()) + { +
      + @* for each item in children of the selected media folder *@ + @foreach (var mediaItem in mediaFolder.Children) + { +
    • + + @mediaItem.Name + +
    • + } +
    + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListAncestorsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListAncestorsFromCurrentPage.cshtml new file mode 100644 index 0000000000..457a47d606 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListAncestorsFromCurrentPage.cshtml @@ -0,0 +1,16 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + +@* Check the current page has ancestors *@ +@if (Model.Ancestors().Any()) +{ +
      + @* For each page in the ancestors collection which have been ordered by Level (so we start with the highest top node first) *@ + @foreach (var page in Model.Ancestors().OrderBy("Level")) + { +
    • @page.Name »
    • + } + + @* Display the current page as the last item in the list *@ +
    • @Model.Name
    • +
    +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesFromChangeableSource.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesFromChangeableSource.cshtml new file mode 100644 index 0000000000..57e4f12245 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesFromChangeableSource.cshtml @@ -0,0 +1,26 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext +@* + === Macro Parameters To Create === + Show:True Alias:nodeId Name:Node ID Type:Content Picker +*@ + + +@{ + var startNodeID = Parameter.nodeId; +} + +@if (startNodeID != null) +{ + @* Get the start node as a dynamic node *@ + var startNode = Library.NodeById(startNodeID); + + if (startNode.Children.Where("Visible").Any()) + { +
      + @foreach (var page in startNode.Children.Where("Visible")) + { +
    • @page.Name
    • + } +
    + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesFromCurrentPage.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesFromCurrentPage.cshtml new file mode 100644 index 0000000000..441b35ef70 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesFromCurrentPage.cshtml @@ -0,0 +1,16 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + + +@* Ensure that the Current Page has children, where the property umbracoNaviHide is not True *@ +@if (Model.Children.Where("Visible").Any()) +{ +
      + @* For each child page under the root node, where the property umbracoNaviHide is not True *@ + @foreach (var childPage in Model.Children.Where("Visible")) + { +
    • + @childPage.Name +
    • + } +
    +} diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByDate.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByDate.cshtml new file mode 100644 index 0000000000..ce0aa17d38 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByDate.cshtml @@ -0,0 +1,11 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + + +
      + @*OrderBy() takes the property to sort by and optionally order desc/asc *@ + + @foreach (var page in Model.Children.Where("Visible").OrderBy("CreateDate desc")) + { +
    • @page.Name
    • + } +
    diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByName.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByName.cshtml new file mode 100644 index 0000000000..57fd127205 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByName.cshtml @@ -0,0 +1,10 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + + +
      + @*OrderBy() takes the property to sort by*@ + @foreach (var page in Model.Children.Where("Visible").OrderBy("Name")) + { +
    • @page.Name
    • + } +
    diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml new file mode 100644 index 0000000000..2f5ebb543b --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml @@ -0,0 +1,22 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + +@* + Macro parameter to be set on the macro + Show:True Alias:propertyAlias Name:Property Alias Type:Textstring +*@ + + +@{ + + @* Get the property alias we want to filter on from the macro parameter *@ + var propertyAlias = Parameter.propertyAlias; + var selection = Model.Children.Where("Visible").OrderBy(propertyAlias) +} + + +
      + @foreach (var page in selection) + { +
    • @page.Name
    • + } +
    diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml new file mode 100644 index 0000000000..9569ef7a7e --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml @@ -0,0 +1,26 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + +@* + This snippet shows how simple it is to fetch only children of a certain Document Type using Razor. Instead of + calling .Children, simply call .AliasOfDocumentType in plural. + For instance .Textpages or .NewsArticles (you can find the alias of your Document Type by editing it in the + Settings section). +*@ + + +@{ + @*Build a query and return the visible items *@ + var selection= Model.Textpages.Where(query).Where("Visible"); +} + +@*Determine if there are any nodes in the selection, then render list *@ +@if(selection.Any()){ + +
      + @foreach(var page in selection){ +
    • @page.Name
    • + } +
    + +} + diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListDescendantsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListDescendantsFromCurrentPage.cshtml new file mode 100644 index 0000000000..32b106757b --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListDescendantsFromCurrentPage.cshtml @@ -0,0 +1,58 @@ +@inherits PartialViewMacroPage +@using Umbraco.Cms.Web +@using Umbraco.Cms.Web.Macros +@using Umbraco.Framework + + +@* Ensure that the Current Page has children, where the property umbracoNaviHide is not True *@ +@if (CurrentPage.Children.Where("umbracoNaviHide != @0", "True").Any()) +{ + @* Get the first page in the children, where the property umbracoNaviHide is not True *@ + var naviLevel = CurrentPage.Children.Where("umbracoNaviHide != @0", "True").First().Level; + + @* Add in level for a CSS hook *@ +
      + @* For each child page under the root node, where the property umbracoNaviHide is not True *@ + @foreach (var childPage in CurrentPage.Children.Where("umbracoNaviHide != @0", "True")) + { +
    • + @childPage.Name + + @* if the current page has any children, where the property umbracoNaviHide is not True *@ + @if (childPage.Children.Where("umbracoNaviHide != @0", "True").Any()) + { + @* Call our helper to display the children *@ + @childPages(childPage.Children) + } +
    • + } +
    +} + + +@helper childPages(dynamic pages) + { + @* Ensure that we have a collection of pages *@ + if (pages.Any()) + { + @* Get the first page in pages and get the level *@ + var naviLevel = pages.First().Level; + + @* Add in level for a CSS hook *@ +
      + @foreach (var page in pages.Where("umbracoNaviHide != @0", "True")) + { +
    • + @page.Name + + @* if the current page has any children, where the property umbracoNaviHide is not True *@ + @if (page.Children.Where("umbracoNaviHide != @0", "True").Any()) + { + @* Call our helper to display the children *@ + @childPages(page.Children) + } +
    • + } +
    + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml new file mode 100644 index 0000000000..7f63b3a98a --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml @@ -0,0 +1,26 @@ + + +@inherits umbraco.MacroEngines.DynamicNodeContext + +@* +Macro Parameters To Create, for this macro to work: +Show:True Alias:mediaId Name:Media Folder ID Type:MediaCurrent +*@ + + +@if (Parameter.mediaId != null) +{ + @* Get the media folder as a dynamic node *@ + var mediaFolder = Library.MediaById(Parameter.mediaId); + + if (mediaFolder.Children.Any()) + { +
      + @* for each item in children of the selected media folder *@ + @foreach (var mediaItem in mediaFolder.Children) + { +
    • @mediaItem.Name
    • + } +
    + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListSubPagesByDateAndLimit.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListSubPagesByDateAndLimit.cshtml deleted file mode 100644 index ff0fda2f58..0000000000 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListSubPagesByDateAndLimit.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@* -LIST SUBPAGES BY LIMIT AND DATETIME -=================================== -This snippet shows how easy it is to combine different queries. It lists the children of the currentpage which is -visible and then grabs a specified number of items sorted by the day they're updated. - -How it works: -- It uses the Take() method to specify a maximum number of items to output -- It adds a OrderBy() to sort the items. You can even combine this, for instance OrderBy("UpdateDate, Name desc") - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! -*@ - -@inherits umbraco.MacroEngines.DynamicNodeContext - -@{ var numberOfItems = 10; } -
      - @foreach (var item in @Model.Children.Where("Visible").OrderBy("UpdateDate").Take(numberOfItems)) - { -
    • @item.Name
    • - } -
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Macro-Parameters.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Macro-Parameters.cshtml deleted file mode 100644 index bf54810cc9..0000000000 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Macro-Parameters.cshtml +++ /dev/null @@ -1,16 +0,0 @@ -@* -MACRO PARAMETERS -=================================== -This snippet is a very simple example on how to grab values specified via Macro Parameters. Macro Parameters are -'attributes' that can be added to Macros (doesn't make sense when Razor is used inline) that makes it possible to -re-use macros for multiple purposes. When you add a Macro Parameter to a Macro, the user can send different values -to the Macro when it's inserted. Macro Parameters in Razor can be accessed via the Parameter property. - -How it works: -- In this example it'll output the value specified in a Macro Parameter with the alias 'Who'. - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! -*@ - - -

    Hello @Parameter.Who

    \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Media.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Media.cshtml deleted file mode 100644 index c41699c8db..0000000000 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Media.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@* -USING MEDIA -================================= -This snippet shows two ways of working with referenced media. Media is referenced from a page using a property with -the type of 'MediaPicker' (or similar for instance MultiNodePicker in uComponents). - -How it works: -- First we check that there's a property on the current page called 'relatedMedia' and that it has a selected value -- In the first example we simply needs the path of the media which is stored in the property 'umbracoFile' and the - media is referenced in the property with the alias of 'relatedMedia'. One line is all it takes! -- In the second example we store the referenced media in a variable as we'd like to get not just the path but also - the name of the media for the friendly alt attribute. - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! -*@ - -@inherits umbraco.MacroEngines.DynamicNodeContext -@if (Model.HasProperty("relatedMedia") && Model.RelatedMedia != 0) { -

    Simple:
    - -

    - -

    Advanced:
    - @{ - var image = @Model.Media("relatedMedia"); - } - @image.Name -

    -} else { -

    - This page doesn't contain a MediaPicker property with the alias of 'RelatedMedia' - or No image is selected on this page -

    -} diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml new file mode 100644 index 0000000000..7e2d252473 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml @@ -0,0 +1,25 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + +@* + Macro to list nodes from a Multinode tree picker, using the pickers default settings. + Content Values stored as xml. + + To get it working with any site's data structure, simply set the selection equal to the property which has the + multinode treepicker. +*@ + +@{ + var selection = Model.PropertyWithPicker; +} + + +@* Lists each selected value from the picker as a link *@ +
      + @foreach(var id in selection){ + + @*For each link, get the node, and display its name and url*@ + var node = Library.NodeById(id.InnerText); + +
    • @node.Name
    • + } +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml index a9d2f0f114..dd8be57d9b 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml @@ -1,36 +1,22 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + @* -NAVIGATION BY LEVEL -================================= -This snippet makes it easy to do navigation based lists! It'll automatically list all children of a page with a certain -level in the hierarchy that's published and visible (it'll filter out any pages with a property named "umbracoNaviHide" -that's set to 'true'. - -How to Customize for re-use (only applies to Macros, not if you insert this snippet directly in a template): -- If you add a Macro Parameter with the alias of "Level" you can use this macro for both level 1 and level 2 navigations -- If you add a Macro Parameter with the alias of "ulClass" you can specify different css classes for the
      element - -How it works: -- The first two lines (var level... and var ulClass) assigns default values if none is specified via Macro Parameters -- Then it finds the correct parent based on the level and assigns it to the 'parent' variable. -- Then it runs through all the visible children in the foreach loop and outputs a list item -- Inside the list item it checks if the page added to the list is a parent of the current page. Then it marks it 'selected' - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! + Macro to display child pages below the root page of a standard website. + Also highlights the current active page/section in the navigation with + the css class "current". *@ -@inherits umbraco.MacroEngines.DynamicNodeContext + @{ - var level = String.IsNullOrEmpty(Parameter.Level) ? 1 : int.Parse(Parameter.Level); - var ulClass = String.IsNullOrEmpty(Parameter.UlClass) ? "" : String.Format(" class=\"{0}\"", Parameter.UlClass); - var parent = @Model.AncestorOrSelf(level); - if (parent != null) { - - @foreach (var item in parent.Children.Where("Visible")) { - var selected = Array.IndexOf(Model.Path.Split(','), item.Id.ToString()) >= 0 ? " class=\"selected\"" : ""; - - @item.Name - - } -
    + @*Get the root of the website *@ + var root = Model.AncestorOrSelf(1); +} + +
      + @foreach (var page in root.Children.Where("Visible")) + { +
    • + @page.Name +
    • } -} +
    diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Paging.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Paging.cshtml deleted file mode 100644 index 71d112cfe0..0000000000 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Paging.cshtml +++ /dev/null @@ -1,83 +0,0 @@ -@* -HOW TO DO PAGING -================================= -This an example of how to do paging of content including a Google style page navigation. You likely want to -modify the query (first line, assigned to the 'pagesToList' variable) and the output made within the foreach -loop (
  • ...
  • ) - -How to Customize for re-use (only applies to Macros, not if you insert this snippet directly in a template): -- You can customize the number of items per page by adding a Macro Parameter with the alias of "ItemsPerPage" -- You can customize the labels of previous/next by adding Macro Parameters with the alias of "PreviousLabel" and - "NextLabel" - -How it works: -- The pages to display is added to the variable 'pagesToList'. To change what pages to list, simply update the query -- The next part assigns the number of items and the previous/next labels using either default values or Macro Parameters -- Then it's using a bit of math to calculate how many pages and what should currently be displayed -- In the first

    element, a summary is printed. This could likely be removed -- In the

      the magic happens. Notice how it's using Skip() and Take() to jump to the relevant items and iterate - over the number of items to display -- In the end it added a Google style page navigation (<>) - - NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! -*@ - -@inherits umbraco.MacroEngines.DynamicNodeContext -@{ - var pagesToList = @Model.Children; - - // configuration - var itemsPerPage = String.IsNullOrEmpty(Parameter.ItemsPerPage) ? 3 : int.Parse(Parameter.ItemsPerPage); - var previousLabel = String.IsNullOrEmpty(Parameter.PreviousLabel) ? "Previous" : Parameter.PreviousLabel; - var nextLabel = String.IsNullOrEmpty(Parameter.NextLabel) ? "Next" : Parameter.NextLabel; - - // paging calculations - var numberOfItems = pagesToList.Count(); - int currentPage = 1; - if (!int.TryParse(HttpContext.Current.Request.QueryString["Page"], out currentPage)) { - currentPage = 1; - } - currentPage--; - var numberOfPages = numberOfItems % itemsPerPage == 0 ? Math.Ceiling((decimal)(numberOfItems / itemsPerPage)) : Math.Ceiling((decimal)(numberOfItems / itemsPerPage))+1; - -

      - Total Items: @numberOfItems
      - Items per Page: @itemsPerPage
      - Pages: @numberOfPages;
      - Current Page: @(currentPage) -

      - -
        - @foreach(var item in pagesToList.Skip(currentPage*itemsPerPage).Take(itemsPerPage)) - { -
      • @item.Name
      • - } -
      - -

      - @{ - // Google style paging links - if (currentPage > 0) { - « @previousLabel - } else { - « @previousLabel - } - - var Pages = Enumerable.Range(1, (int)numberOfPages); - foreach(var number in Pages) { - if (number-1 != currentPage) { - @number - } else { - @number - } - @Html.Raw("  "); - } - - if (currentPage < Pages.Count()-1) { - @nextLabel » - } else { - @nextLabel » - } - } -

      -} diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SelectChildrenByDocumentType.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SelectChildrenByDocumentType.cshtml deleted file mode 100644 index c7d2652526..0000000000 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SelectChildrenByDocumentType.cshtml +++ /dev/null @@ -1,16 +0,0 @@ -@* -LIST CHILDREN BY TYPE -================================= -This snippet shows how simple it is to fetch only children of a certain Document Type using Razor. Instead of -calling .Children, simply call .AliasOfDocumentType (even works in plural for readability)! -For instance .Textpage or .Textpages (you can find the alias of your Document Type by editing it in the -Settings section). - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! -*@ -
        - @foreach (var item in @Model.Textpages.Where("Visible")) - { -
      • @item.Name
      • -} -
      \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml index e25ac0e108..d9d2ec35df 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml @@ -1,44 +1,38 @@ -@* -SITEMAP -================================= -This snippet generates a complete sitemap of all pages that are published and visible (it'll filter out any -pages with a property named "umbracoNaviHide" that's set to 'true'). It's also a great example on how to make -helper methods in Razor and how to pass values to your '.Where' filters. - -How to Customize for re-use (only applies to Macros, not if you insert this snippet directly in a template): -- If you add a Macro Parameter with the alias of "MaxLevelForSitemap" which specifies how deep in the hierarchy to traverse - -How it works: -- The first line (var maxLevelForSitemap) assigns default values if none is specified via Macro Parameters -- Next is a helper method 'traverse' which uses recursion to keep making new lists for each level in the sitemap -- Inside the the 'traverse' method there's an example of using a 'Dictionary' to pass the 'maxLevelForSitemap' to - the .Where filter -- Finally the 'traverse' method is called taking the very top node of the website by calling AncesterOrSelf() - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! -*@ - @inherits umbraco.MacroEngines.DynamicNodeContext +@{ + @* Walk up the tree from the current page to get the root node *@ + var rootNode = Model.AncestorOrself(1); +} + +@*Render the sitemap by passing the root node to the traverse helper*@ +
      + @traverse(@Model.AncestorOrSelf()) +
      + + + +@*Helper method to travers through all descendants*@ @helper traverse(dynamic node){ + +@*If a MaxLevelForSitemap parameter is passed to the macro, otherwise default to 4 levels*@ var maxLevelForSitemap = String.IsNullOrEmpty(Parameter.MaxLevelForSitemap) ? 4 : int.Parse(Parameter.MaxLevelForSitemap); -var values = new Dictionary(); -values.Add("maxLevelForSitemap", maxLevelForSitemap) ; +@*Select visible children *@ +var items = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap); - var items = node.Children.Where("Visible").Where("Level <= maxLevelForSitemap", values); - if (items.Count() > 0) { + +@*If any items are returned, render a list *@ +if (items.Any()) {
        @foreach (var item in items) { -
      • +
      • @item.Name + + @*Run the traverse helper again *@ @traverse(item)
      • }
      } -} -
      - @traverse(@Model.AncestorOrSelf()) -
      - +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/TwitterFeed.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/TwitterFeed.cshtml new file mode 100644 index 0000000000..86cbe66514 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/TwitterFeed.cshtml @@ -0,0 +1,217 @@ +@inherits umbraco.MacroEngines.DynamicNodeContext + + +@{ + @* + Macro Parameters To Create + Show:True Alias:twitterUsername Name:Twitter Username Type:Textstring Defaults to: umbraco + Show:True Alias:includeRTs Name:Include Retweets Type:Textstring Defaults to: false + Show:True Alias:excludeReplies Name:Exclude Replies Type:Textstring Defaults to: false + Show:True Alias:noTweets Name:Number of Tweets Type:Integer Defaults to: 1 + *@ + + var twitterUsername = String.IsNullOrEmpty(Parameter.twitterUsername) ? "umbraco" : Parameter.twitterUsername; + var includeRTs = String.IsNullOrEmpty(Parameter.includeRTs) ? false : Parameter.includeRTs; + var excludeReplies = String.IsNullOrEmpty(Parameter.excludeReplies) ? false : Parameter.excludeReplies; + var noTweets = String.IsNullOrEmpty(Parameter.noTweets) ? 1 : Parameter.noTweets; + + + @* Twitter JSON URL *@ + var twitterURL = string.Format( + "https://api.twitter.com/1/statuses/user_timeline.json?screen_name={0}&include_rts={1}&exclude_replies={2}&include_entities=1&count={3}", + twitterUsername, + includeRTs, + excludeReplies, + noTweets); +} + + +@* Fetch the JSON from Twitters API *@ +@using (var client = new System.Net.WebClient()) +{ + @* Fetch the JSON from Twitter *@ + var response = client.DownloadString(new Uri(twitterURL)); + + @* Decode the JSON so we can interate over it *@ + var tweets = Json.Decode(response); + +
        + @foreach (var tweet in tweets) + { +
      • + @* Tweet with formatted links *@ +

        @formatLinks(tweet.text, tweet.entities)

        + +

        + @* Format Tweet Date and ouput as 24/03/12 @14:05 *@ + @formatDate(tweet.created_at).ToString("dd/MM/yy @ HH:mm") +

        + +

        + @* Profile Image *@ + @tweet.user.Name + + @* Your real name (not profile name) *@ + @tweet.user.name +

        + + @* Google Map (if tweet has geo info) *@ + @if (tweet.geo != null) + { + @Html.Raw(displayMap(tweet.geo)); + } + + @* Dislay Image (if tweet has image attached *@ + @if (tweet.entities.media != null) + { + + + + } +
      • + } +
      +} + +@functions +{ + DateTime formatDate(string twitterDate) + { + //Example tweet date + //Fri Mar 02 16:09:35 +0000 2012 + //ddd MMM dd HH:mm:ss zz00 yyyy + DateTime tweetDate = DateTime.ParseExact(twitterDate, "ddd MMM dd HH:mm:ss zz00 yyyy", null); + + return tweetDate; + } + + String displayMap(dynamic geo) + { + //Get the lat & long values + var tweetLat = geo.coordinates[0]; + var tweetLong = geo.coordinates[1]; + + //Format the string to return the image + var googleMap = string.Format("http://maps.googleapis.com/maps/api/staticmap?center={0},{1}&zoom=14&size=250x250&maptype=roadmap&sensor=false&markers={0}, {1}", tweetLat, tweetLong); + var mapImage = string.Format("map", googleMap); + + return mapImage; + } + + IHtmlString formatLinks(string tweet, dynamic entities) + { + //A List of tweet entities so we can sort all of them + IList tweetEntities = new List(); + + //Get URLs + var links = entities.urls; + + //Check we have links to loop over + if (links != null) + { + //For each link in the collection of links + foreach (var link in links) + { + var startPosition = link.indices[0]; + var endPosition = link.indices[1]; + var length = endPosition - startPosition; + + var url = link.url; //The short t.co link + var displayURL = link.display_url; //A friendly version of the full link (may be truncated) + + var newText = string.Format("{1}", url, displayURL); + var oldText = tweet.Substring(startPosition, length); + + + //Create a new entity + tweetEntity entity = new tweetEntity(); + entity.startPosition = startPosition; + entity.endPosition = endPosition; + entity.newText = newText; + entity.oldText = oldText; + + //Add it to the collection + tweetEntities.Add(entity); + } + } + + + //Get user mentions (@umbraco) + var mentions = entities.user_mentions; + + //Check we have mentions to loop over + if (mentions != null) + { + //For each mention in the collection of mentions + foreach (var mention in mentions) + { + var startPosition = mention.indices[0]; + var endPosition = mention.indices[1]; + var length = endPosition - startPosition; + + var username = mention.screen_name; + + var newText = string.Format("@{0}", username); + var oldText = tweet.Substring(startPosition, length); + + //Create a new entity + tweetEntity entity = new tweetEntity(); + entity.startPosition = startPosition; + entity.endPosition = endPosition; + entity.newText = newText; + entity.oldText = oldText; + + //Add to collection + tweetEntities.Add(entity); + } + } + + //Get hashtags + var hashtags = entities.hashtags; + + //Check we have hash to loop over + if (hashtags != null) + { + foreach (var hash in hashtags) + { + var startPosition = hash.indices[0]; + var endPosition = hash.indices[1]; + var length = endPosition - startPosition; + var hashtag = hash.text; + + var newText = string.Format("#{0}", hashtag); + var oldText = tweet.Substring(startPosition, length); + + //Create a new entity + tweetEntity entity = new tweetEntity(); + entity.startPosition = startPosition; + entity.endPosition = endPosition; + entity.newText = newText; + entity.oldText = oldText; + + //Add to collection + tweetEntities.Add(entity); + } + } + + //For each item in the tweet entities in reverse order + //If we update the string in reverse order the remaining start/end indexs will still be correct + foreach (var item in tweetEntities.OrderByDescending(x => x.startPosition)) + { + //Lets update the tweet text + tweet = tweet.Replace(item.oldText, item.newText); + } + + + //Return the new tweet with all the links added in + return Html.Raw(tweet); + } + + public class tweetEntity + { + public int startPosition { get; set; } + public int endPosition { get; set; } + public string oldText { get; set; } + public string newText { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/UsingRelatedLinks.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/UsingRelatedLinks.cshtml deleted file mode 100644 index 628dcfb51b..0000000000 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/UsingRelatedLinks.cshtml +++ /dev/null @@ -1,37 +0,0 @@ -@* -USING RELATED LINKS (AND OTHER XML BASED TYPES -============================================== -This snippet shows how to work with properties that stores multiple values in XML such as the "Related Links" data type. -When the Razor (or in fact the 'DynamicNode') detected XML, it automatically makes it possible to navigate the xml by -using the name of the XML elements as properties. Be aware that the first xml element (the container) is always skipped -and that the properties are case sensitive! - -How it works: -- First we check if there's a property on the current page (Model) named 'relatedLinks' -- Then we loop through the XML elements of the property 'RelatedLinks' (ie. all the links) -- For each link we check if it should be opened in a new window (stored in an XML attribute called 'newwindow' which is - automatically translated into a property '.newwindow' by DynamicNode -- Then we test if the link type is a internal or external link, and if it's an internal link we use the NiceUrl helper - method to convert the id of the page to a SEO friendly url - -NOTE: It is safe to remove this comment (anything between @ * * @), the code that generates the list is only the below! -*@ - -@inherits umbraco.MacroEngines.DynamicNodeContext - -@{ - if (Model.HasProperty("relatedLinks")) { -
        - @foreach (var link in @Model.RelatedLinks) { - string target = link.newwindow == "1" ? " target=\"_blank\"" : ""; -
      • - @if (link.type == "internal") { - @link.title - } else { - @link.title - } -
      • - } -
      - } -} diff --git a/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx b/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx index 1d37205240..be119d814f 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx +++ b/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx @@ -1,22 +1,30 @@ <%@ Page MasterPageFile="../masterpages/umbracoPage.Master" Language="c#" CodeBehind="EditTemplate.aspx.cs" - ValidateRequest="false" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Umbraco.Settings.EditTemplate" %> - + ValidateRequest="false" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Umbraco.Settings.EditTemplate" %> <%@ Import Namespace="Umbraco.Core.IO" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - - - - - + diff --git a/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx b/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx index abc9bdb271..14d049c27d 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx +++ b/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx @@ -1,8 +1,13 @@ <%@ Page Language="C#" MasterPageFile="../../masterpages/umbracoPage.Master" AutoEventWireup="true" CodeBehind="editScript.aspx.cs" Inherits="umbraco.cms.presentation.settings.scripts.editScript" - ValidateRequest="False" %> + ValidateRequest="False" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> + + + + + "; - macroProperties.Controls.Add(noProps); + //var noProps = new Literal(); + //noProps.Text = ""; + //macroProperties.Controls.Add(noProps); } else { //if we have properties, we'll render the controls for them... - foreach (cms.businesslogic.macro.MacroProperty mp in _m.Properties) + foreach (cms.businesslogic.macro.MacroProperty mp in MacroObject.Properties) { var macroAssembly = mp.Type.Assembly; var macroType = mp.Type.Type; @@ -126,7 +126,7 @@ namespace umbraco.dialogs else { IRecordsReader macroRenderings; - if (helper.Request("editor") != "") + if (Request.GetItemAsString("editor") != "") macroRenderings = SqlHelper.ExecuteReader("select macroAlias, macroName from cmsMacro where macroUseInEditor = 1 order by macroName"); else macroRenderings = SqlHelper.ExecuteReader("select macroAlias, macroName from cmsMacro order by macroName"); @@ -138,32 +138,8 @@ namespace umbraco.dialogs macroRenderings.Close(); } } - else - { - ScriptManager.RegisterOnSubmitStatement(Page, Page.GetType(), "myHandlerKey", "Umbraco.Dialogs.EditMacro.getInstance().updateMacro()"); - } - } - - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion + } /// /// pl_edit control. diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs index afbd497d4d..aeab148ef7 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs @@ -261,26 +261,7 @@ namespace umbraco.dialogs var documentId = int.Parse(helper.Request("id")); var document = new Document(documentId); document.Move(int.Parse(helper.Request("copyTo"))); - if (document.Published) - { - //TODO HACK - Have to get the Document again, to get the new path from the database.. - document = new Document(documentId); - - document.Publish(new umbraco.BusinessLogic.User(0)); - //using library.publish to support load balancing. - //umbraco.library.PublishSingleNode(d.Id); - umbraco.library.UpdateDocumentCache(document.Id); - - //PPH added handling of load balanced moving of multiple nodes... - if (document.HasChildren) - handleChildNodes(document); - - //Using the general Refresh content method instead as it supports load balancing. - //we only need to do this if the node is actually published. - library.RefreshContent(); - } - - document.Save(); //stub to save stuff to the db. + library.RefreshContent(); } else { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs index 5c892ea101..08fa0f6ed8 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs @@ -1,6 +1,8 @@ using System; using System.Web.UI; using System.Web.UI.WebControls; +using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.Persistence.Caching; using umbraco.BusinessLogic.Actions; using umbraco.IO; @@ -321,15 +323,23 @@ namespace umbraco.cms.presentation library.UpdateDocumentCache(_document.Id); ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editContentPublishedHeader", null), ui.Text("speechBubbles", "editContentPublishedText", null)); - littPublishStatus.Text = ui.Text("content", "lastPublished", base.getUser()) + ": " + _document.VersionDate.ToString() + "
      "; + littPublishStatus.Text = string.Format("{0}: {1}
      ", ui.Text("content", "lastPublished", base.getUser()), _document.VersionDate.ToString()); if (base.getUser().GetPermissions(_document.Path).IndexOf("U") > -1) UnPublish.Visible = true; _documentHasPublishedVersion = _document.HasPublishedVersion(); - foreach (var descendant in _document.GetDescendants().Cast().Where(descendant => descendant.HasPublishedVersion())) - library.UpdateDocumentCache(descendant.Id); + var descendants = ApplicationContext.Current.Services.ContentService.GetDescendants(_document.Id); + var publishableDescendants = descendants.Where(descendant => descendant.HasPublishedVersion()).ToList(); + if(publishableDescendants.Any()) + { + foreach (var descendant in publishableDescendants) + { + library.UpdateDocumentCache(descendant.Id); + } + library.RefreshContent(); + } } else { @@ -337,13 +347,10 @@ namespace umbraco.cms.presentation } } else + { ClientTools.ShowSpeechBubble(speechBubbleIcon.warning, ui.Text("publish"), ui.Text("speechBubbles", "editContentPublishedFailedByParent")); - - // page cache disabled... - // cms.businesslogic.cache.Cache.ClearCacheObjectTypes("umbraco.page"); - - - // Update links + + } } } @@ -382,7 +389,7 @@ namespace umbraco.cms.presentation void UpdateNiceUrls() { - if (!_documentHasPublishedVersion || _document.Published == false) + if (_documentHasPublishedVersion == false) { UpdateNiceUrlProperties("" + ui.Text("content", "itemNotPublished", base.getUser()) + "", null); return; @@ -405,7 +412,7 @@ namespace umbraco.cms.presentation while (parent != null && parent.Published); if (parent == null) // oops - internal error - niceUrlText = "" + ui.Text("content", "parentNotPublished", "???", base.getUser()) + ""; + niceUrlText = "" + ui.Text("content", "parentNotPublishedAnomaly", base.getUser()) + ""; else niceUrlText = "" + ui.Text("content", "parentNotPublished", parent.Text, base.getUser()) + ""; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs index 0c3be42e00..170d2324c5 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs @@ -128,6 +128,8 @@ namespace umbraco.settings else dt.RemoveDefaultTemplate(); + dt.Save(); + bindTemplates(); } else diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/scripts/editScript.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/scripts/editScript.aspx index abc9bdb271..14d049c27d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/scripts/editScript.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/scripts/editScript.aspx @@ -1,8 +1,13 @@ <%@ Page Language="C#" MasterPageFile="../../masterpages/umbracoPage.Master" AutoEventWireup="true" CodeBehind="editScript.aspx.cs" Inherits="umbraco.cms.presentation.settings.scripts.editScript" - ValidateRequest="False" %> + ValidateRequest="False" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> + + + + +