diff --git a/.gitignore b/.gitignore
index 284703ec00..0073675d82 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,3 +128,4 @@ src/*.boltdata/
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.config.js
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
src/umbraco.sln.ide/*
+build/UmbracoCms.*/
diff --git a/build/Build.bat b/build/Build.bat
index 0f0a6f1711..7410396ead 100644
--- a/build/Build.bat
+++ b/build/Build.bat
@@ -2,9 +2,18 @@
IF NOT EXIST UmbracoVersion.txt (
ECHO UmbracoVersion.txt missing!
GOTO :showerror
-)
-SET /p release=NUL
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder%
+..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder%
+..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder%
ECHO Removing the belle build folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
@@ -33,20 +45,22 @@ ECHO Performing MSBuild and producing Umbraco binaries zip files
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment%
ECHO Adding dummy files to include in the NuGet package so that empty folders actually get created
-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\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\Views\Partials\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\Views\MacroPartials\dummy.txt
+SET dummytext=This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete.
+ECHO %dummytext% > .\_BuildOutput\WebApp\App_Code\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\App_Data\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\App_Plugins\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\css\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\masterpages\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\media\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\scripts\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\usercontrols\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\Views\Partials\dummy.txt
+ECHO %dummytext% > .\_BuildOutput\WebApp\Views\MacroPartials\dummy.txt
ECHO Adding Web.config transform files to the NuGet package
-ren .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
-ren .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
+REN .\_BuildOutput\WebApp\MacroScripts\Web.config Web.config.transform
+REN .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
+REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
ECHO Packing the NuGet release files
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %version%
diff --git a/build/Build.proj b/build/Build.proj
index 30d794fb3f..20c36e18aa 100644
--- a/build/Build.proj
+++ b/build/Build.proj
@@ -156,8 +156,8 @@
-
-
+
+
diff --git a/build/NuSpecs/UmbracoCms.Core.AllBinaries.nuspec b/build/NuSpecs/UmbracoCms.Core.AllBinaries.nuspec
index fa3881dc92..517f0d441e 100644
--- a/build/NuSpecs/UmbracoCms.Core.AllBinaries.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.AllBinaries.nuspec
@@ -1,74 +1,74 @@
-
- UmbracoCms.Core.AllBinaries
- 6.1.2
- Umbraco Cms Core All Binaries
- Morten Christensen
- Umbraco HQ
- http://umbraco.codeplex.com/license
- http://umbraco.com/
- http://umbraco.com/media/357769/100px_transparent.png
- false
- Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.
- Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms
- en-US
- umbraco
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ UmbracoCms.Core.AllBinaries
+ 6.1.2
+ Umbraco Cms Core All Binaries
+ Morten Christensen
+ Umbraco HQ
+ http://umbraco.codeplex.com/license
+ http://umbraco.com/
+ http://umbraco.com/media/357769/100px_transparent.png
+ false
+ Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.
+ Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms
+ en-US
+ umbraco
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/NuSpecs/UmbracoCms.Core.Symbols.nuspec b/build/NuSpecs/UmbracoCms.Core.Symbols.nuspec
index 7acbfed18c..29f7365017 100644
--- a/build/NuSpecs/UmbracoCms.Core.Symbols.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.Symbols.nuspec
@@ -1,71 +1,71 @@
-
- UmbracoCms.Core
- 7.0.0
- Umbraco Cms Core Binaries
- Umbraco HQ
- Umbraco HQ
- http://umbraco.codeplex.com/license
- http://umbraco.com/
- http://umbraco.com/media/357769/100px_transparent.png
- false
- Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.
- Contains the core assemblies needed to run Umbraco Cms
- en-US
- umbraco
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ UmbracoCms.Core
+ 7.0.0
+ Umbraco Cms Core Binaries
+ Umbraco HQ
+ Umbraco HQ
+ http://umbraco.codeplex.com/license
+ http://umbraco.com/
+ http://umbraco.com/media/357769/100px_transparent.png
+ false
+ Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.
+ Contains the core assemblies needed to run Umbraco Cms
+ en-US
+ umbraco
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec
index 46819783f0..c680dce419 100644
--- a/build/NuSpecs/UmbracoCms.Core.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.nuspec
@@ -1,74 +1,73 @@
-
- UmbracoCms.Core
- 7.0.0
- Umbraco Cms Core Binaries
- Umbraco HQ
- Umbraco HQ
- http://umbraco.codeplex.com/license
- http://umbraco.com/
- http://umbraco.com/media/357769/100px_transparent.png
- false
- Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.
- Contains the core assemblies needed to run Umbraco Cms
- en-US
- umbraco
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ UmbracoCms.Core
+ 7.0.0
+ Umbraco Cms Core Binaries
+ Umbraco HQ
+ Umbraco HQ
+ http://umbraco.codeplex.com/license
+ http://umbraco.com/
+ http://umbraco.com/media/357769/100px_transparent.png
+ false
+ Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.
+ Contains the core assemblies needed to run Umbraco Cms
+ en-US
+ umbraco
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index b4b264e463..a184b79d5d 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -1,43 +1,43 @@
-
- UmbracoCms
- 7.0.0
- Umbraco Cms
- Umbraco HQ
- Umbraco HQ
- http://umbraco.codeplex.com/license
- http://umbraco.com/
- http://umbraco.com/media/357769/100px_transparent.png
- false
- Installs Umbraco Cms in your Visual Studio ASP.NET project
- Installs Umbraco Cms in your Visual Studio ASP.NET project
- en-US
- umbraco
-
-
-
-
-
+
+ UmbracoCms
+ 7.0.0
+ Umbraco Cms
+ Umbraco HQ
+ Umbraco HQ
+ http://umbraco.codeplex.com/license
+ http://umbraco.com/
+ http://umbraco.com/media/357769/100px_transparent.png
+ false
+ Installs Umbraco Cms in your Visual Studio ASP.NET project
+ Installs Umbraco Cms in your Visual Studio ASP.NET project
+ en-US
+ umbraco
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/NuSpecs/UmbracoExamine.PDF.nuspec b/build/NuSpecs/UmbracoExamine.PDF.nuspec
index 3b6869cde2..ca4acdcc36 100644
--- a/build/NuSpecs/UmbracoExamine.PDF.nuspec
+++ b/build/NuSpecs/UmbracoExamine.PDF.nuspec
@@ -1,23 +1,23 @@
-
- UmbracoExamine.PDF
- 0.7.0
- Umbraco HQ
- Umbraco HQ
- http://umbraco.codeplex.com/license
- http://umbraco.com/
- http://umbraco.com/media/357769/100px_transparent.png
- false
- UmbracoExmine.PDF
- umbraco
-
-
-
-
-
-
-
-
-
+
+ UmbracoExamine.PDF
+ 0.7.0
+ Umbraco HQ
+ Umbraco HQ
+ http://umbraco.codeplex.com/license
+ http://umbraco.com/
+ http://umbraco.com/media/357769/100px_transparent.png
+ false
+ UmbracoExmine.PDF
+ umbraco
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/NuSpecs/tools/Dashboard.config.install.xdt b/build/NuSpecs/tools/Dashboard.config.install.xdt
index a1960e5797..1f5bbb0164 100644
--- a/build/NuSpecs/tools/Dashboard.config.install.xdt
+++ b/build/NuSpecs/tools/Dashboard.config.install.xdt
@@ -26,11 +26,15 @@
views/dashboard/developer/developerdashboardvideos.html
-
+
+
+
+
+
views/dashboard/developer/examinemanagement.html
-
+
diff --git a/build/NuSpecs/tools/Readme.txt b/build/NuSpecs/tools/Readme.txt
index 9964be2311..d5121fe521 100644
--- a/build/NuSpecs/tools/Readme.txt
+++ b/build/NuSpecs/tools/Readme.txt
@@ -1,25 +1,32 @@
+ _ _ __ __ ____ _____ _____ ____
+ | | | | \/ | _ \| __ \ /\ / ____/ __ \
+ | | | | \ / | |_) | |__) | / \ | | | | | |
+ | | | | |\/| | _ <| _ / / /\ \| | | | | |
+ | |__| | | | | |_) | | \ \ / ____ | |___| |__| |
+ \____/|_| |_|____/|_| \_/_/ \_\_____\____/
+
+----------------------------------------------------
+
A note about running Umbraco from Visual Studio.
Don't forget to build!
-BETA Notice: We've done our best to transform your config files, we are testing this in 7.2 beta and would love to
-hear back if it didn't work so we can improve this feature. We'd love to see your before and after config files
-(make sure to delete sensitive data like passwords and API keys) in the issue tracker at http://issues.umbraco.org/
-Remember, we backed up your files in App_Data\NuGetBackup so you can find the original file before it was transformed.
-
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
file (and config files in the config folder).
-We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
-App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or a package
-might have added. Only the existing files were overwritten. If you customized anything then make sure to do a compare
-and merge with the NuGetBackup folder.
+We've done our best to transform your configuration files but in case something is not quite right: remember we
+backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
-This nuget package includes build targets that extends the creation of a deploy package, which is generated by
+We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
+App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
+a package might have added. Only the existing files were overwritten. If you customized anything then make
+sure to do a compare and merge with the NuGetBackup folder.
+
+This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
-These things will now be automatically included when creating a deploy package or publishing to the file system:
-umbraco, umbraco_client, config\splashes and global.asax.
+The following items will now be automatically included when creating a deploy package or publishing to the file
+system: umbraco, umbraco_client, config\splashes and global.asax.
Please read the release notes on our.umbraco.org:
http://our.umbraco.org/contribute/releases
diff --git a/build/NuSpecs/tools/Web.config.install.xdt b/build/NuSpecs/tools/Web.config.install.xdt
index 4dd1cb4391..6215d405fd 100644
--- a/build/NuSpecs/tools/Web.config.install.xdt
+++ b/build/NuSpecs/tools/Web.config.install.xdt
@@ -6,7 +6,9 @@
-
+
+
+
@@ -154,10 +156,25 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/NuSpecs/tools/install.ps1 b/build/NuSpecs/tools/install.ps1
index 9c7fa57f82..5b9aee0dfb 100644
--- a/build/NuSpecs/tools/install.ps1
+++ b/build/NuSpecs/tools/install.ps1
@@ -118,7 +118,7 @@ if ($project) {
$lines = $lastOperation -split "`r`n"
- $installMatch = $lines | ? { $_.StartsWith("------- installing...umbracocms ") } | select -first 1
+ $installMatch = $lines | ? { $_.Contains("...umbracocms ") } | select -first 1
if ($installMatch)
{
diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt
index fa5fce04b3..bba582b4dc 100644
--- a/build/UmbracoVersion.txt
+++ b/build/UmbracoVersion.txt
@@ -1 +1,2 @@
+# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
8.0.0
\ No newline at end of file
diff --git a/src/SQLCE4Umbraco/SqlCEHelper.cs b/src/SQLCE4Umbraco/SqlCEHelper.cs
index 1787c05912..26a781360e 100644
--- a/src/SQLCE4Umbraco/SqlCEHelper.cs
+++ b/src/SQLCE4Umbraco/SqlCEHelper.cs
@@ -229,5 +229,17 @@ namespace SqlCE4Umbraco
return new SqlCeDataReaderHelper(SqlCeApplicationBlock.ExecuteReader(ConnectionString, CommandType.Text,
commandText, parameters));
}
+
+
+ internal IRecordsReader ExecuteReader(string commandText)
+ {
+ return ExecuteReader(commandText, new SqlCEParameter(string.Empty, string.Empty));
+ }
+
+
+ internal int ExecuteNonQuery(string commandText)
+ {
+ return ExecuteNonQuery(commandText, new SqlCEParameter(string.Empty, string.Empty));
+ }
}
}
\ No newline at end of file
diff --git a/src/SQLCE4Umbraco/app.config b/src/SQLCE4Umbraco/app.config
index 53f3b4c80b..8f828418f3 100644
--- a/src/SQLCE4Umbraco/app.config
+++ b/src/SQLCE4Umbraco/app.config
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs
index f513deb9f5..f2152afaab 100644
--- a/src/Umbraco.Core/IO/FileSystemExtensions.cs
+++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs
@@ -2,17 +2,18 @@
using System.IO;
namespace Umbraco.Core.IO
-{
+{
public static class FileSystemExtensions
{
- public static long GetSize(this IFileSystem fs, string path)
+ public static long GetSize(this IFileSystem fs, string path)
{
- using (var s = fs.OpenFile(path))
+ using (var file = fs.OpenFile(path))
{
- var size = s.Length;
- s.Close();
-
- return size;
+ using (var sr = new StreamReader(file))
+ {
+ var str = sr.ReadToEnd();
+ return str.Length;
+ }
}
}
@@ -25,14 +26,14 @@ namespace Umbraco.Core.IO
}
public static string GetExtension(this IFileSystem fs, string path)
- {
- return Path.GetExtension(fs.GetFullPath(path));
- }
+ {
+ return Path.GetExtension(fs.GetFullPath(path));
+ }
public static string GetFileName(this IFileSystem fs, string path)
- {
- return Path.GetFileName(fs.GetFullPath(path));
- }
+ {
+ return Path.GetFileName(fs.GetFullPath(path));
+ }
//TODO: Currently this is the only way to do this
internal static void CreateFolder(this IFileSystem fs, string folderPath)
diff --git a/src/Umbraco.Core/Mandate.cs b/src/Umbraco.Core/Mandate.cs
index 54d589b90a..7ae47fd5c0 100644
--- a/src/Umbraco.Core/Mandate.cs
+++ b/src/Umbraco.Core/Mandate.cs
@@ -8,7 +8,7 @@ namespace Umbraco.Core
///
/// Helper class for mandating values, for example on method parameters.
///
- internal static class Mandate
+ public static class Mandate
{
///
/// Mandates that the specified parameter is not null.
diff --git a/src/Umbraco.Core/Manifest/ManifestParser.cs b/src/Umbraco.Core/Manifest/ManifestParser.cs
index 49839828fd..fbc91de898 100644
--- a/src/Umbraco.Core/Manifest/ManifestParser.cs
+++ b/src/Umbraco.Core/Manifest/ManifestParser.cs
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Manifest
public IEnumerable GetManifests()
{
//get all Manifest.js files in the appropriate folders
- var manifestFileContents = GetAllManfifestFileContents(_pluginsDir);
+ var manifestFileContents = GetAllManifestFileContents(_pluginsDir);
return CreateManifests(manifestFileContents.ToArray());
}
@@ -69,7 +69,7 @@ namespace Umbraco.Core.Manifest
///
///
///
- private IEnumerable GetAllManfifestFileContents(DirectoryInfo currDir)
+ private IEnumerable GetAllManifestFileContents(DirectoryInfo currDir)
{
var depth = FolderDepth(_pluginsDir, currDir);
@@ -79,7 +79,7 @@ namespace Umbraco.Core.Manifest
var result = new List();
foreach (var d in dirs)
{
- result.AddRange(GetAllManfifestFileContents(d));
+ result.AddRange(GetAllManifestFileContents(d));
}
return result;
}
diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs
index ec763d07fa..fc53a21c3f 100644
--- a/src/Umbraco.Core/Models/ContentType.cs
+++ b/src/Umbraco.Core/Models/ContentType.cs
@@ -15,11 +15,11 @@ namespace Umbraco.Core.Models
{
private int _defaultTemplate;
private IEnumerable _allowedTemplates;
-
+
///
/// Constuctor for creating a ContentType with the parent's id.
///
- /// You usually only want to use this for creating ContentTypes at the root.
+ /// Only use this for creating ContentTypes at the root (with ParentId -1).
///
public ContentType(int parentId) : base(parentId)
{
@@ -31,14 +31,26 @@ namespace Umbraco.Core.Models
///
/// Use this to ensure inheritance from parent.
///
- public ContentType(IContentType parent) : base(parent)
- {
- _allowedTemplates = new List();
- }
+ [Obsolete("This method is obsolete, use ContentType(IContentType parent, string alias) instead.", false)]
+ public ContentType(IContentType parent) : this(parent, null)
+ {
+ }
+
+ ///
+ /// Constuctor for creating a ContentType with the parent as an inherited type.
+ ///
+ /// Use this to ensure inheritance from parent.
+ ///
+ ///
+ public ContentType(IContentType parent, string alias)
+ : base(parent, alias)
+ {
+ _allowedTemplates = new List();
+ }
private static readonly PropertyInfo DefaultTemplateSelector = ExpressionHelper.GetPropertyInfo(x => x.DefaultTemplateId);
private static readonly PropertyInfo AllowedTemplatesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedTemplates);
-
+
///
/// Gets or sets the alias of the default Template.
///
@@ -95,7 +107,7 @@ namespace Umbraco.Core.Models
}
DefaultTemplateId = template.Id;
- if(_allowedTemplates.Any(x => x != null && x.Id == template.Id) == false)
+ if (_allowedTemplates.Any(x => x != null && x.Id == template.Id) == false)
{
var templates = AllowedTemplates.ToList();
templates.Add(template);
@@ -129,7 +141,7 @@ namespace Umbraco.Core.Models
{
base.AddingEntity();
- if(Key == Guid.Empty)
+ if (Key == Guid.Empty)
Key = Guid.NewGuid();
}
@@ -140,7 +152,7 @@ namespace Umbraco.Core.Models
///
[Obsolete("Use DeepCloneWithResetIdentities instead")]
public IContentType Clone(string alias)
- {
+ {
return DeepCloneWithResetIdentities(alias);
}
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index 50212f2722..30ac526f4a 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
@@ -15,6 +16,7 @@ namespace Umbraco.Core.Models
///
[Serializable]
[DataContract(IsReference = true)]
+ [DebuggerDisplay("Id: {Id}, Name: {Name}, Alias: {Alias}")]
public abstract class ContentTypeBase : Entity, IContentTypeBase
{
private Lazy _parentId;
@@ -48,17 +50,22 @@ namespace Umbraco.Core.Models
_additionalData = new Dictionary();
}
- protected ContentTypeBase(IContentTypeBase parent)
+ protected ContentTypeBase(IContentTypeBase parent) : this(parent, null)
{
- Mandate.ParameterNotNull(parent, "parent");
+ }
- _parentId = new Lazy(() => parent.Id);
- _allowedContentTypes = new List();
- _propertyGroups = new PropertyGroupCollection();
+ protected ContentTypeBase(IContentTypeBase parent, string alias)
+ {
+ Mandate.ParameterNotNull(parent, "parent");
+
+ _alias = alias;
+ _parentId = new Lazy(() => parent.Id);
+ _allowedContentTypes = new List();
+ _propertyGroups = new PropertyGroupCollection();
_propertyTypes = new PropertyTypeCollection();
_propertyTypes.CollectionChanged += PropertyTypesChanged;
_additionalData = new Dictionary();
- }
+ }
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId);
@@ -492,7 +499,6 @@ namespace Umbraco.Core.Models
/// Alias of the to remove
public void RemovePropertyType(string propertyTypeAlias)
{
-
//check if the property exist in one of our collections
if (PropertyGroups.Any(group => group.PropertyTypes.Any(pt => pt.Alias == propertyTypeAlias))
|| _propertyTypes.Any(x => x.Alias == propertyTypeAlias))
@@ -519,6 +525,7 @@ namespace Umbraco.Core.Models
public void RemovePropertyGroup(string propertyGroupName)
{
PropertyGroups.RemoveItem(propertyGroupName);
+ OnPropertyChanged(PropertyGroupCollectionSelector);
}
///
diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
index 3bebb59959..ac358481f1 100644
--- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
@@ -22,11 +22,16 @@ namespace Umbraco.Core.Models
}
protected ContentTypeCompositionBase(IContentTypeComposition parent)
- : base(parent)
+ : this(parent, null)
{
- AddContentType(parent);
}
+ protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias)
+ : base(parent, alias)
+ {
+ AddContentType(parent);
+ }
+
private static readonly PropertyInfo ContentTypeCompositionSelector =
ExpressionHelper.GetPropertyInfo>(
x => x.ContentTypeComposition);
@@ -76,6 +81,9 @@ namespace Umbraco.Core.Models
if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists)))
return false;
+ if (string.IsNullOrEmpty(Alias) == false && Alias.Equals(contentType.Alias))
+ return false;
+
if (ContentTypeCompositionExists(contentType.Alias) == false)
{
//Before we actually go ahead and add the ContentType as a Composition we ensure that we don't
diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs
index d337b82bbe..3513e4e031 100644
--- a/src/Umbraco.Core/Models/File.cs
+++ b/src/Umbraco.Core/Models/File.cs
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Models
}
///
- /// Gets or sets the Path to the File from the root of the site
+ /// Gets or sets the Path to the File from the root of the file's associated IFileSystem
///
[DataMember]
public virtual string Path
@@ -99,6 +99,11 @@ namespace Umbraco.Core.Models
}
}
+ ///
+ /// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
+ ///
+ public string VirtualPath { get; set; }
+
///
/// Boolean indicating whether the file could be validated
///
diff --git a/src/Umbraco.Core/Models/IFile.cs b/src/Umbraco.Core/Models/IFile.cs
index b0cc96b56d..b4d0b75a79 100644
--- a/src/Umbraco.Core/Models/IFile.cs
+++ b/src/Umbraco.Core/Models/IFile.cs
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Models
string Alias { get; }
///
- /// Gets or sets the Path to the File from the root of the site
+ /// Gets or sets the Path to the File from the root of the file's associated IFileSystem
///
string Path { get; set; }
@@ -28,6 +28,11 @@ namespace Umbraco.Core.Models
///
string Content { get; set; }
+ ///
+ /// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
+ ///
+ string VirtualPath { get; set; }
+
///
/// Boolean indicating whether the file could be validated
///
diff --git a/src/Umbraco.Core/Models/IPartialView.cs b/src/Umbraco.Core/Models/IPartialView.cs
new file mode 100644
index 0000000000..01127ce22a
--- /dev/null
+++ b/src/Umbraco.Core/Models/IPartialView.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Core.Models
+{
+ public interface IPartialView : IFile
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs
index eb8e0b7a66..0c86f5d1fe 100644
--- a/src/Umbraco.Core/Models/Macro.cs
+++ b/src/Umbraco.Core/Models/Macro.cs
@@ -9,6 +9,7 @@ using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Umbraco.Core.IO;
using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Strings;
namespace Umbraco.Core.Models
{
@@ -48,7 +49,7 @@ namespace Umbraco.Core.Models
Id = id;
UseInEditor = useInEditor;
CacheDuration = cacheDuration;
- Alias = alias;
+ Alias = alias.ToCleanString(CleanStringType.Alias);
Name = name;
ControlType = controlType;
ControlAssembly = controlAssembly;
@@ -87,7 +88,7 @@ namespace Umbraco.Core.Models
{
UseInEditor = useInEditor;
CacheDuration = cacheDuration;
- Alias = alias;
+ Alias = alias.ToCleanString(CleanStringType.Alias);
Name = name;
ControlType = controlType;
ControlAssembly = controlAssembly;
@@ -207,7 +208,7 @@ namespace Umbraco.Core.Models
{
SetPropertyValueAndDetectChanges(o =>
{
- _alias = value;
+ _alias = value.ToCleanString(CleanStringType.Alias);
return _alias;
}, _alias, AliasSelector);
}
diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs
index d8e3c58538..052e231136 100644
--- a/src/Umbraco.Core/Models/MediaType.cs
+++ b/src/Umbraco.Core/Models/MediaType.cs
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Models
///
/// Constuctor for creating a MediaType with the parent's id.
///
- /// You usually only want to use this for creating MediaTypes at the root.
+ /// Only use this for creating MediaTypes at the root (with ParentId -1).
///
public MediaType(int parentId) : base(parentId)
{
@@ -24,10 +24,21 @@ namespace Umbraco.Core.Models
///
/// Use this to ensure inheritance from parent.
///
- public MediaType(IMediaType parent) : base(parent)
+ public MediaType(IMediaType parent) : this(parent, null)
{
}
+ ///
+ /// Constuctor for creating a MediaType with the parent as an inherited type.
+ ///
+ /// Use this to ensure inheritance from parent.
+ ///
+ ///
+ public MediaType(IMediaType parent, string alias)
+ : base(parent, alias)
+ {
+ }
+
///
/// Method to call when Entity is being saved
///
diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs
index 1706ba5ab1..c4c0362acc 100644
--- a/src/Umbraco.Core/Models/Member.cs
+++ b/src/Umbraco.Core/Models/Member.cs
@@ -20,7 +20,6 @@ namespace Umbraco.Core.Models
private string _email;
private string _rawPasswordValue;
private object _providerUserKey;
- private Type _userTypeKey;
///
/// Constructor for creating an empty Member object
@@ -114,7 +113,6 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email);
private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.RawPasswordValue);
private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKey);
- private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType);
///
/// Gets or sets the Username
@@ -502,38 +500,7 @@ namespace Umbraco.Core.Models
}
}
- ///
- /// Gets or sets the type of the provider user key.
- ///
- ///
- /// The type of the provider user key.
- ///
- [IgnoreDataMember]
- internal Type ProviderUserKeyType
- {
- get
- {
- return _userTypeKey;
- }
- private set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _userTypeKey = value;
- return _userTypeKey;
- }, _userTypeKey, UserTypeKeySelector);
- }
- }
-
- ///
- /// Sets the type of the provider user key.
- ///
- /// The type.
- internal void SetProviderUserKeyType(Type type)
- {
- ProviderUserKeyType = type;
- }
-
+
///
/// Method to call when Entity is being saved
///
diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs
index 6620843ab5..74a879be81 100644
--- a/src/Umbraco.Core/Models/MemberType.cs
+++ b/src/Umbraco.Core/Models/MemberType.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
@@ -21,7 +20,12 @@ namespace Umbraco.Core.Models
MemberTypePropertyTypes = new Dictionary();
}
- public MemberType(IContentTypeComposition parent) : base(parent)
+ public MemberType(IContentTypeComposition parent) : this(parent, null)
+ {
+ }
+
+ public MemberType(IContentTypeComposition parent, string alias)
+ : base(parent, alias)
{
MemberTypePropertyTypes = new Dictionary();
}
diff --git a/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs
index 6008c0ae02..2289983033 100644
--- a/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs
+++ b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs
@@ -7,9 +7,9 @@ namespace Umbraco.Core.Models.Membership
{
internal static class MembershipUserExtensions
{
- internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName)
+ internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName, bool providerKeyAsGuid = false)
{
- var membershipMember = new UmbracoMembershipMember(member, providerName);
+ var membershipMember = new UmbracoMembershipMember(member, providerName, providerKeyAsGuid);
return membershipMember;
}
diff --git a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs
index 1b8c7f5393..0f02f4f73b 100644
--- a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs
+++ b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs
@@ -1,5 +1,6 @@
using System;
using System.Web.Security;
+using Umbraco.Core.Configuration;
namespace Umbraco.Core.Models.Membership
{
@@ -25,7 +26,7 @@ namespace Umbraco.Core.Models.Membership
//NOTE: We are not calling the base constructor which will validate that a provider with the specified name exists which causes issues with unit tests. The ctor
// validation for that doesn't need to be there anyways (have checked the source).
- public UmbracoMembershipMember(IMembershipUser member, string providerName)
+ public UmbracoMembershipMember(IMembershipUser member, string providerName, bool providerKeyAsGuid = false)
{
_member = member;
//NOTE: We are copying the values here so that everything is consistent with how the underlying built-in ASP.Net membership user
@@ -37,7 +38,7 @@ namespace Umbraco.Core.Models.Membership
if (member.PasswordQuestion != null)
_passwordQuestion = member.PasswordQuestion.Trim();
_providerName = providerName;
- _providerUserKey = member.ProviderUserKey;
+ _providerUserKey = providerKeyAsGuid ? member.ProviderUserKey : member.Id;
_comment = member.Comments;
_isApproved = member.IsApproved;
_isLockedOut = member.IsLockedOut;
diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs
index ba9b89d779..c9b55bd937 100644
--- a/src/Umbraco.Core/Models/Membership/User.cs
+++ b/src/Umbraco.Core/Models/Membership/User.cs
@@ -58,7 +58,6 @@ namespace Umbraco.Core.Models.Membership
private IUserType _userType;
private string _name;
- private Type _userTypeKey;
private List _addedSections;
private List _removedSections;
private ObservableCollection _sectionCollection;
@@ -80,7 +79,6 @@ namespace Umbraco.Core.Models.Membership
private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartMediaId);
private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedSections);
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
- private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType);
private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username);
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email);
@@ -101,37 +99,6 @@ namespace Umbraco.Core.Models.Membership
set { throw new NotSupportedException("Cannot set the provider user key for a user"); }
}
- ///
- /// Gets or sets the type of the provider user key.
- ///
- ///
- /// The type of the provider user key.
- ///
- [IgnoreDataMember]
- internal Type ProviderUserKeyType
- {
- get
- {
- return _userTypeKey;
- }
- private set
- {
- SetPropertyValueAndDetectChanges(o =>
- {
- _userTypeKey = value;
- return _userTypeKey;
- }, _userTypeKey, UserTypeKeySelector);
- }
- }
-
- ///
- /// Sets the type of the provider user key.
- ///
- /// The type.
- internal void SetProviderUserKeyType(Type type)
- {
- ProviderUserKeyType = type;
- }
[DataMember]
public string Username
diff --git a/src/Umbraco.Core/Models/PartialView.cs b/src/Umbraco.Core/Models/PartialView.cs
index e470d55037..0f0279d8d3 100644
--- a/src/Umbraco.Core/Models/PartialView.cs
+++ b/src/Umbraco.Core/Models/PartialView.cs
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Models
///
[Serializable]
[DataContract(IsReference = true)]
- internal class PartialView : File
+ public class PartialView : File, IPartialView
{
//public PartialView(): base(string.Empty)
//{
diff --git a/src/Umbraco.Core/Models/PropertyGroupCollection.cs b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
index 6e1847856b..399fa22be6 100644
--- a/src/Umbraco.Core/Models/PropertyGroupCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
@@ -71,16 +71,34 @@ namespace Umbraco.Core.Models
{
using (new WriteLock(_addLocker))
{
- var key = GetKeyForItem(item);
- if (key != null)
+ //Note this is done to ensure existig groups can be renamed
+ if (item.HasIdentity && item.Id > 0)
{
- var exists = this.Contains(key);
+ var exists = this.Contains(item.Id);
if (exists)
{
- SetItem(IndexOfKey(key), item);
+ var keyExists = this.Contains(item.Name);
+ if(keyExists)
+ throw new Exception(string.Format("Naming conflict: Changing the name of PropertyGroup '{0}' would result in duplicates", item.Name));
+
+ SetItem(IndexOfKey(item.Id), item);
return;
}
}
+ else
+ {
+ var key = GetKeyForItem(item);
+ if (key != null)
+ {
+ var exists = this.Contains(key);
+ if (exists)
+ {
+ SetItem(IndexOfKey(key), item);
+ return;
+ }
+ }
+ }
+
base.Add(item);
OnAdd.IfNotNull(x => x.Invoke());//Could this not be replaced by a Mandate/Contract for ensuring item is not null
@@ -99,6 +117,11 @@ namespace Umbraco.Core.Models
return this.Any(x => x.Name == groupName);
}
+ public bool Contains(int id)
+ {
+ return this.Any(x => x.Id == id);
+ }
+
public void RemoveItem(string propertyGroupName)
{
var key = IndexOfKey(propertyGroupName);
@@ -119,6 +142,18 @@ namespace Umbraco.Core.Models
return -1;
}
+ public int IndexOfKey(int id)
+ {
+ for (var i = 0; i < this.Count; i++)
+ {
+ if (this[i].Id == id)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
protected override string GetKeyForItem(PropertyGroup item)
{
return item.Name;
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index fb7e82b27e..a19b7d98c2 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -1,9 +1,9 @@
using System;
+using System.Diagnostics;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
@@ -14,6 +14,7 @@ namespace Umbraco.Core.Models
///
[Serializable]
[DataContract(IsReference = true)]
+ [DebuggerDisplay("Id: {Id}, Name: {Name}, Alias: {Alias}")]
public class PropertyType : Entity, IEquatable
{
private readonly bool _isExplicitDbType;
diff --git a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
index a89879d8b5..f9aadf4963 100644
--- a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
@@ -41,6 +41,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("uniqueID")]
[NullSetting(NullSetting = NullSettings.Null)]
+ [Index(IndexTypes.NonClustered, Name = "IX_umbracoNodeUniqueID")]
public Guid? UniqueId { get; set; }
[Column("text")]
diff --git a/src/Umbraco.Core/Models/TaggableObjectTypes.cs b/src/Umbraco.Core/Models/TaggableObjectTypes.cs
index be019410e5..ae54bc02bd 100644
--- a/src/Umbraco.Core/Models/TaggableObjectTypes.cs
+++ b/src/Umbraco.Core/Models/TaggableObjectTypes.cs
@@ -5,6 +5,7 @@
///
public enum TaggableObjectTypes
{
+ All,
Content,
Media,
Member
diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs
index 93960d70c4..0621c83a72 100644
--- a/src/Umbraco.Core/Models/UserExtensions.cs
+++ b/src/Umbraco.Core/Models/UserExtensions.cs
@@ -1,11 +1,36 @@
using System;
using System.Globalization;
+using System.Linq;
using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
- internal static class UserExtensions
+ public static class UserExtensions
{
+ ///
+ /// Returns the culture info associated with this user, based on the language they're assigned to in the back office
+ ///
+ ///
+ ///
+ ///
+ public static CultureInfo GetUserCulture(this IUser user, ILocalizedTextService textService)
+ {
+ if (user == null) throw new ArgumentNullException("user");
+ if (textService == null) throw new ArgumentNullException("textService");
+ return GetUserCulture(user.Language, textService);
+ }
+
+ internal static CultureInfo GetUserCulture(string userLanguage, ILocalizedTextService textService)
+ {
+ return textService.GetSupportedCultures()
+ .FirstOrDefault(culture =>
+ //match on full name first
+ culture.Name.InvariantEquals(userLanguage.Replace("_", "-")) ||
+ //then match on the 2 letter name
+ culture.TwoLetterISOLanguageName.InvariantEquals(userLanguage));
+ }
+
///
/// Checks if the user has access to the content item based on their start noe
///
diff --git a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
index f2223d338e..6aa3cce51c 100644
--- a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
+++ b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
@@ -23,7 +23,7 @@ namespace Umbraco.Core.ObjectResolution
where TResolver : ResolverBase
{
#region Constructors
-
+
///
/// Initializes a new instance of the class with an empty list of objects,
/// with creation of objects based on an HttpRequest lifetime scope.
@@ -31,9 +31,11 @@ namespace Umbraco.Core.ObjectResolution
/// The lifetime scope of instantiated objects, default is per Application.
/// If is per HttpRequest then there must be a current HttpContext.
/// is per HttpRequest but the current HttpContext is null.
- protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
- : base(scope)
- { }
+ protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
+ : base(scope)
+ {
+ Initialize();
+ }
///
/// Initializes a new instance of the class with an empty list of objects,
@@ -41,9 +43,11 @@ namespace Umbraco.Core.ObjectResolution
///
/// The HttpContextBase corresponding to the HttpRequest.
/// is null.
- protected LazyManyObjectsResolverBase(HttpContextBase httpContext)
- : base(httpContext)
- { }
+ protected LazyManyObjectsResolverBase(HttpContextBase httpContext)
+ : base(httpContext)
+ {
+ Initialize();
+ }
///
/// Initializes a new instance of the class with an initial list
@@ -101,23 +105,37 @@ namespace Umbraco.Core.ObjectResolution
private readonly List> _lazyTypeList = new List>();
private readonly List>> _typeListProducerList = new List>>();
private readonly List _excludedTypesList = new List();
+ private Lazy> _resolvedTypes = null;
+
+ private void Initialize()
+ {
+ _resolvedTypes = new Lazy>(() =>
+ {
+ var resolvedTypes = new List();
+
+ // get the types by evaluating the lazy & producers
+ var types = new List();
+ types.AddRange(_lazyTypeList.Select(x => x.Value));
+ types.AddRange(_typeListProducerList.SelectMany(x => x()));
+
+ // we need to validate each resolved type now since we could
+ // not do it before evaluating the lazy & producers
+ foreach (var type in types.Where(x => _excludedTypesList.Contains(x) == false))
+ {
+ AddValidAndNoDuplicate(resolvedTypes, type);
+ }
+
+ return resolvedTypes;
+ });
+ }
- private List _resolvedTypes = null;
- private readonly ReaderWriterLockSlim _resolvedTypesLock = new ReaderWriterLockSlim();
-
///
/// Gets a value indicating whether the resolver has resolved types to create instances from.
///
/// To be used in unit tests.
public bool HasResolvedTypes
{
- get
- {
- using (new ReadLock(_resolvedTypesLock))
- {
- return _resolvedTypes != null;
- }
- }
+ get { return _resolvedTypes.IsValueCreated; }
}
///
@@ -126,32 +144,7 @@ namespace Umbraco.Core.ObjectResolution
/// When called, will get the types from the lazy list.
protected override IEnumerable InstanceTypes
{
- get
- {
- using (var lck = new UpgradeableReadLock(_resolvedTypesLock))
- {
- if (_resolvedTypes == null)
- {
- lck.UpgradeToWriteLock();
-
- _resolvedTypes = new List();
-
- // get the types by evaluating the lazy & producers
- var types = new List();
- types.AddRange(_lazyTypeList.Select(x => x.Value));
- types.AddRange(_typeListProducerList.SelectMany(x => x()));
-
- // we need to validate each resolved type now since we could
- // not do it before evaluating the lazy & producers
- foreach (var type in types.Where(x => !_excludedTypesList.Contains(x)))
- {
- AddValidAndNoDuplicate(_resolvedTypes, type);
- }
- }
-
- return _resolvedTypes;
- }
- }
+ get { return _resolvedTypes.Value; }
}
///
diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
index 8bb5995891..95ff923c58 100644
--- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
+++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
@@ -15,7 +15,7 @@ namespace Umbraco.Core.ObjectResolution
where TResolved : class
where TResolver : ResolverBase
{
- private IEnumerable _applicationInstances = null;
+ private Lazy> _applicationInstances = null;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private readonly string _httpContextKey;
private readonly List _instanceTypes = new List();
@@ -47,6 +47,8 @@ namespace Umbraco.Core.ObjectResolution
if (scope == ObjectLifetimeScope.HttpRequest)
_httpContextKey = this.GetType().FullName;
_instanceTypes = new List();
+
+ InitializeAppInstances();
}
///
@@ -64,6 +66,8 @@ namespace Umbraco.Core.ObjectResolution
_httpContextKey = this.GetType().FullName;
CurrentHttpContext = httpContext;
_instanceTypes = new List();
+
+ InitializeAppInstances();
}
///
@@ -94,6 +98,11 @@ namespace Umbraco.Core.ObjectResolution
}
#endregion
+ private void InitializeAppInstances()
+ {
+ _applicationInstances = new Lazy>(() => CreateInstances().ToArray());
+ }
+
///
/// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen.
///
@@ -178,30 +187,17 @@ namespace Umbraco.Core.ObjectResolution
switch (LifetimeScope)
{
case ObjectLifetimeScope.HttpRequest:
- // create new instances per HttpContext
- using (var l = new UpgradeableReadLock(_lock))
- {
- // create if not already there
- if (CurrentHttpContext.Items[_httpContextKey] == null)
- {
- l.UpgradeToWriteLock();
- CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray();
- }
- return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
- }
- case ObjectLifetimeScope.Application:
- // create new instances per application
- using (var l = new UpgradeableReadLock(_lock))
+ // create new instances per HttpContext
+ if (CurrentHttpContext.Items[_httpContextKey] == null)
{
- // create if not already there
- if (_applicationInstances == null)
- {
- l.UpgradeToWriteLock();
- _applicationInstances = CreateInstances().ToArray();
- }
- return _applicationInstances;
+ CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray();
}
+ return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
+
+ case ObjectLifetimeScope.Application:
+
+ return _applicationInstances.Value;
case ObjectLifetimeScope.Transient:
default:
diff --git a/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs b/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs
index 006821e2b3..ea7c8ab8f9 100644
--- a/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs
@@ -13,12 +13,21 @@ namespace Umbraco.Core.Persistence.Mappers
internal abstract void BuildMap();
- internal string Map(string propertyName)
+ internal string Map(string propertyName, bool throws = false)
{
DtoMapModel dtoTypeProperty;
- return PropertyInfoCache.TryGetValue(propertyName, out dtoTypeProperty)
- ? GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo)
- : string.Empty;
+ if (PropertyInfoCache.TryGetValue(propertyName, out dtoTypeProperty))
+ {
+ return GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
+ }
+ else
+ {
+ if (throws)
+ {
+ throw new InvalidOperationException("Could not get the value with the key " + propertyName + " from the property info cache, keys available: " + string.Join(", ", PropertyInfoCache.Keys));
+ }
+ return string.Empty;
+ }
}
internal void CacheMap(Expression> sourceMember, Expression> destinationMember)
@@ -30,7 +39,12 @@ namespace Umbraco.Core.Persistence.Mappers
internal DtoMapModel ResolveMapping(Expression> sourceMember, Expression> destinationMember)
{
var source = ExpressionHelper.FindProperty(sourceMember);
- var destination = ExpressionHelper.FindProperty(destinationMember) as PropertyInfo;
+ var destination = (PropertyInfo)ExpressionHelper.FindProperty(destinationMember);
+
+ if (destination == null)
+ {
+ throw new InvalidOperationException("The 'destination' returned was null, cannot resolve the mapping");
+ }
return new DtoMapModel(typeof(TDestination), destination, source.Name);
}
diff --git a/src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs b/src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs
index b897e53895..d351b8abb3 100644
--- a/src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs
@@ -12,8 +12,8 @@ namespace Umbraco.Core.Persistence.Mappers
SourcePropertyName = sourcePropertyName;
}
- public string SourcePropertyName { get; set; }
- public Type Type { get; set; }
- public PropertyInfo PropertyInfo { get; set; }
+ public string SourcePropertyName { get; private set; }
+ public Type Type { get; private set; }
+ public PropertyInfo PropertyInfo { get; private set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs b/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs
index 4b6b513bf2..007ddbd014 100644
--- a/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs
@@ -41,11 +41,6 @@ namespace Umbraco.Core.Persistence.Mappers
{
return byAttribute.Result;
}
-
- //static mapper registration if not using attributes, could be something like this:
- //if (type == typeof (UserType))
- // return new UserTypeMapper();
-
throw new Exception("Invalid Type: A Mapper could not be resolved based on the passed in Type");
});
}
@@ -56,31 +51,18 @@ namespace Umbraco.Core.Persistence.Mappers
///
///
private Attempt TryGetMapperByAttribute(Type entityType)
- {
- //get all BaseMapper types that have a MapperFor attribute:
- var assignedMapperTypes = InstanceTypes;
-
+ {
//check if any of the mappers are assigned to this type
- var mapper = assignedMapperTypes.FirstOrDefault(
- x => x.GetCustomAttributes(false)
+ var mapper = Values.FirstOrDefault(
+ x => x.GetType().GetCustomAttributes(false)
.Any(m => m.EntityType == entityType));
if (mapper == null)
{
return Attempt.Fail();
}
- try
- {
- var instance = Activator.CreateInstance(mapper) as BaseMapper;
- return instance != null
- ? Attempt.Succeed(instance)
- : Attempt.Fail();
- }
- catch (Exception ex)
- {
- LogHelper.Error(typeof(MappingResolver), "Could not instantiate mapper of type " + mapper, ex);
- return Attempt.Fail(ex);
- }
+
+ return Attempt.Succeed(mapper);
}
internal string GetMapping(Type type, string propertyName)
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs
index e4425673f2..60828c5a5c 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
}
else
{
- //If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these default constraints
+ //If we are on SQLServer, we need to delete default constraints by name, older versions of umbraco did not name these default constraints
// consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue:
// http://issues.umbraco.org/issue/U4-4133
var sqlServerSyntaxProvider = new SqlServerSyntaxProvider();
@@ -51,16 +51,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
Delete.Column("macroPropertyHidden").FromTable("cmsMacroProperty");
- if (Context.CurrentDatabaseProvider != DatabaseProviders.SqlServer)
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
Delete.ForeignKey().FromTable("cmsMacroProperty").ForeignColumn("macroPropertyType").ToTable("cmsMacroPropertyType").PrimaryColumn("id");
}
else
{
- //If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these key constraints
- // consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue:
- // http://issues.umbraco.org/issue/U4-4133
-
+ //Before we try to delete this constraint, we'll see if it exists first, some older schemas never had it and some older schema's had this named
+ // differently than the default.
+
var keyConstraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct();
var constraint = keyConstraints
.SingleOrDefault(x => x.Item1 == "cmsMacroProperty" && x.Item2 == "macroPropertyType" && x.Item3.InvariantStartsWith("PK_") == false);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
index a9d4a148c8..31cfeb7997 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
@@ -31,15 +31,14 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable();
//drop the foreign key on umbracoNode. Must drop foreign key first before primary key can be removed in MySql.
- if (Context.CurrentDatabaseProvider != DatabaseProviders.SqlServer)
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id");
}
else
{
- //If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these key constraints
- // consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue:
- // http://issues.umbraco.org/issue/U4-4133
+ //Before we try to delete this constraint, we'll see if it exists first, some older schemas never had it and some older schema's had this named
+ // differently than the default.
var constraint = constraints
.SingleOrDefault(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "nodeId" && x.Item3.InvariantStartsWith("PK_") == false);
@@ -62,7 +61,12 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
}
else
{
- Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship");
+ //lookup the PK by name
+ var pkName = constraints.FirstOrDefault(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantStartsWith("PK_"));
+ if (pkName != null)
+ {
+ Delete.PrimaryKey(pkName.Item3).FromTable("cmsTagRelationship");
+ }
}
}
@@ -137,22 +141,6 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
.OnDelete(Rule.None)
.OnUpdate(Rule.None);
- //Some very old schemas don't have an index on the cmsContent.nodeId column, I'm not actually sure when it was added but
- // it is absolutely required to exist in order to add this foreign key, so we'll need to check it's existence
- // this came to light from this issue: http://issues.umbraco.org/issue/U4-4133
- var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
- .Select(x => new DbIndexDefinition()
- {
- TableName = x.Item1,
- IndexName = x.Item2,
- ColumnName = x.Item3,
- IsUnique = x.Item4
- }).ToArray();
- if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContent")) == false)
- {
- Create.Index("IX_cmsContent").OnTable("cmsContent").OnColumn("nodeId").Ascending().WithOptions().Unique();
- }
-
//now we need to add a foreign key to the nodeId column to cmsContent (intead of the original umbracoNode)
Create.ForeignKey("FK_cmsTagRelationship_cmsContent")
.FromTable("cmsTagRelationship")
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AssignMissingKeysAndIndexes.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AssignMissingKeysAndIndexes.cs
new file mode 100644
index 0000000000..7be58853c9
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AssignMissingKeysAndIndexes.cs
@@ -0,0 +1,93 @@
+using System.Data;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
+{
+ ///
+ /// I'm not actually sure how this is possible but I've come across one install that was missing these PKs
+ /// and it wasn't a MySQL install.
+ /// see: http://issues.umbraco.org/issue/U4-5707
+ ///
+ [Migration("7.0.0", 0, GlobalSettings.UmbracoMigrationName)]
+ public class AssignMissingKeysAndIndexes : MigrationBase
+ {
+ public override void Up()
+ {
+
+ //Some very old schemas don't have an index on the cmsContent.nodeId column, I'm not actually sure when it was added but
+ // it is absolutely required to exist in order to have it as a foreign key reference, so we'll need to check it's existence
+ // this came to light from this issue: http://issues.umbraco.org/issue/U4-4133
+ var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContent")) == false)
+ {
+ Create.Index("IX_cmsContent").OnTable("cmsContent").OnColumn("nodeId").Ascending().WithOptions().Unique();
+ }
+
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.SqlServer
+ || Context.CurrentDatabaseProvider == DatabaseProviders.SqlServerCE)
+ {
+ var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
+
+ //This should be 2 because this table has 2 keys
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsPreviewXml") && x.Item3.InvariantStartsWith("PK_")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsContentPreviewXml")
+ .OnTable("cmsPreviewXml")
+ .Columns(new[] { "nodeId", "versionId" });
+ }
+
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsTags") && x.Item3.InvariantStartsWith("PK_")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsTags")
+ .OnTable("cmsTags")
+ .Columns(new[] { "id" });
+ }
+
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsStylesheetProperty") && x.Item3.InvariantStartsWith("PK_")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsStylesheetProperty")
+ .OnTable("cmsStylesheetProperty")
+ .Columns(new[] { "nodeId" });
+ }
+
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsStylesheet") && x.Item3.InvariantStartsWith("PK_")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsStylesheet")
+ .OnTable("cmsStylesheet")
+ .Columns(new[] { "nodeId" });
+
+ Create.ForeignKey("FK_cmsStylesheet_umbracoNode_id").FromTable("cmsStylesheet").ForeignColumn("nodeId")
+ .ToTable("umbracoNode").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None);
+ }
+
+ if (constraints.Count(x => x.Item1.InvariantEquals("cmsMember") && x.Item3.InvariantStartsWith("PK_")) == 0)
+ {
+ Create.PrimaryKey("PK_cmsMember")
+ .OnTable("cmsMember")
+ .Columns(new[] { "nodeId" });
+
+ Create.ForeignKey("FK_cmsMember_umbracoNode_id").FromTable("cmsMember").ForeignColumn("nodeId")
+ .ToTable("umbracoNode").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None);
+
+ Create.ForeignKey("FK_cmsMember_cmsContent_nodeId").FromTable("cmsMember").ForeignColumn("nodeId")
+ .ToTable("cmsContent").PrimaryColumn("nodeId").OnDeleteOrUpdate(Rule.None);
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ //don't do anything, these keys should have always existed!
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddIndexToUmbracoNodeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddIndexToUmbracoNodeTable.cs
new file mode 100644
index 0000000000..90cd2be8c2
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddIndexToUmbracoNodeTable.cs
@@ -0,0 +1,45 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero
+{
+ [Migration("7.2.0", 3, GlobalSettings.UmbracoMigrationName)]
+ public class AddIndexToUmbracoNodeTable : MigrationBase
+ {
+ private readonly bool _skipIndexCheck;
+
+ internal AddIndexToUmbracoNodeTable(bool skipIndexCheck)
+ {
+ _skipIndexCheck = skipIndexCheck;
+ }
+
+ public AddIndexToUmbracoNodeTable()
+ {
+ }
+
+ public override void Up()
+ {
+ var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
+ //make sure it doesn't already exist
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")) == false)
+ {
+ Create.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Ascending().WithOptions().NonClustered();
+ }
+ }
+
+ public override void Down()
+ {
+ Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs
index 8343a9dde5..b15f5eba58 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs
@@ -1,3 +1,5 @@
+using System.Linq;
+using AutoMapper;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -11,9 +13,20 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero
{
public override void Up()
{
- //To text
- var textType = SqlSyntaxContext.SqlSyntaxProvider.GetSpecialDbType(SpecialDbTypes.NTEXT);
- Alter.Table("cmsDataTypePreValues").AlterColumn("value").AsCustom(textType).Nullable();
+ var columns = SqlSyntaxContext.SqlSyntaxProvider.GetColumnsInSchema(Context.Database).Distinct().ToArray();
+
+ //Check if it's already text
+ if (columns.Any(x => x.ColumnName.InvariantEquals("value") && x.TableName.InvariantEquals("cmsDataTypePreValues")
+ //mysql check
+ && (x.DataType.InvariantEquals("longtext") == false
+ //sql server check
+ && x.DataType.InvariantEquals("ntext") == false)))
+ {
+ //To text
+ var textType = SqlSyntaxContext.SqlSyntaxProvider.GetSpecialDbType(SpecialDbTypes.NTEXT);
+ Alter.Table("cmsDataTypePreValues").AlterColumn("value").AsCustom(textType).Nullable();
+ }
+
}
public override void Down()
diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs
index 21f97643ec..2d57f99285 100644
--- a/src/Umbraco.Core/Persistence/PetaPoco.cs
+++ b/src/Umbraco.Core/Persistence/PetaPoco.cs
@@ -562,7 +562,14 @@ namespace Umbraco.Core.Persistence
{
object val = cmd.ExecuteScalarWithRetry();
OnExecutedCommand(cmd);
- return (T)Convert.ChangeType(val, typeof(T));
+
+ if (val == null || val == DBNull.Value)
+ return default(T);
+
+ Type t = typeof(T);
+ Type u = Nullable.GetUnderlyingType(t);
+
+ return (T)Convert.ChangeType(val, u ?? t);
}
}
finally
@@ -843,7 +850,7 @@ namespace Umbraco.Core.Persistence
public IEnumerable Query(Sql sql) { return Query(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql.SQL, sql.Arguments); }
// Automagically guess the property relationships between various POCOs and create a delegate that will set them up
- object GetAutoMapper(Type[] types)
+ Delegate GetAutoMapper(Type[] types)
{
// Build a key
var kb = new StringBuilder();
@@ -858,7 +865,7 @@ namespace Umbraco.Core.Persistence
RWLock.EnterReadLock();
try
{
- object mapper;
+ Delegate mapper;
if (AutoMappers.TryGetValue(key, out mapper))
return mapper;
}
@@ -872,7 +879,7 @@ namespace Umbraco.Core.Persistence
try
{
// Try again
- object mapper;
+ Delegate mapper;
if (AutoMappers.TryGetValue(key, out mapper))
return mapper;
@@ -945,22 +952,53 @@ namespace Umbraco.Core.Persistence
throw new InvalidOperationException(string.Format("Couldn't find split point between {0} and {1}", typeThis, typeNext));
}
+
// Instance data used by the Multipoco factory delegate - essentially a list of the nested poco factories to call
public class MultiPocoFactory
{
- public List m_Delegates;
- public Delegate GetItem(int index) { return m_Delegates[index]; }
+
+ public MultiPocoFactory(IEnumerable dels)
+ {
+ Delegates = new List(dels);
+ }
+ private List Delegates { get; set; }
+ private Delegate GetItem(int index) { return Delegates[index]; }
+
+ ///
+ /// Calls the delegate at the specified index and returns its values
+ ///
+ ///
+ ///
+ ///
+ private object CallDelegate(int index, IDataReader reader)
+ {
+ var d = GetItem(index);
+ var output = d.DynamicInvoke(reader);
+ return output;
+ }
+
+ ///
+ /// Calls the callback delegate and passes in the output of all delegates as the parameters
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public TRet CallCallback(Delegate callback, IDataReader dr, int count)
+ {
+ var args = new List
///
- ///
+ ///
/// If set to true and a back office identity is not found and not authenticated, this will attempt to authenticate the
- /// request just as is done in the Umbraco module and then set the current identity if it is valid
+ /// request just as is done in the Umbraco module and then set the current identity if it is valid.
+ /// Just like in the UmbracoModule, if this is true then the user's culture will be assigned to the request.
///
///
/// Returns the current back office identity if an admin is authenticated otherwise null
@@ -89,12 +90,20 @@ namespace Umbraco.Core.Security
if (http.User == null) return null; //there's no user at all so no identity
var identity = http.User.Identity as UmbracoBackOfficeIdentity;
if (identity != null) return identity;
+
if (authenticateRequestIfNotFound == false) return null;
+
//even if authenticateRequestIfNotFound is true we cannot continue if the request is actually authenticated
// which would mean something strange is going on that it is not an umbraco identity.
if (http.User.Identity.IsAuthenticated) return null;
+
+ //So the user is not authed but we've been asked to do the auth if authenticateRequestIfNotFound = true,
+ // which might occur in old webforms style things or for routes that aren't included as a back office request.
+ // in this case, we are just reverting to authing using the cookie.
+
+ // TODO: Even though this is in theory legacy, we have legacy bits laying around and we'd need to do the auth based on
+ // how the Module will eventually do it (by calling in to any registered authenticators).
- //now we just need to try to authenticate the current request
var ticket = http.GetUmbracoAuthTicket();
if (http.AuthenticateCurrentRequest(ticket, true))
{
diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs
index 00dd4ddb0e..93069b7bd9 100644
--- a/src/Umbraco.Core/Services/ContentService.cs
+++ b/src/Umbraco.Core/Services/ContentService.cs
@@ -407,7 +407,7 @@ namespace Umbraco.Core.Services
{
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
- var query = Query.Builder.Where(x => x.Level == level && !x.Path.StartsWith("-20"));
+ var query = Query.Builder.Where(x => x.Level == level && !x.Path.StartsWith(Constants.System.RecycleBinContent.ToInvariantString()));
var contents = repository.GetByQuery(query);
return contents;
@@ -460,7 +460,7 @@ namespace Umbraco.Core.Services
/// An Enumerable list of objects
public IEnumerable GetAncestors(IContent content)
{
- var ids = content.Path.Split(',').Where(x => x != "-1" && x != content.Id.ToString(CultureInfo.InvariantCulture)).Select(int.Parse).ToArray();
+ var ids = content.Path.Split(',').Where(x => x != Constants.System.Root.ToInvariantString() && x != content.Id.ToString(CultureInfo.InvariantCulture)).Select(int.Parse).ToArray();
if (ids.Any() == false)
return new List();
@@ -506,8 +506,8 @@ namespace Umbraco.Core.Services
{
var query = Query.Builder;
- //if the id is -1, then just get all
- if (id > 0)
+ //if the id is System Root, then just get all
+ if (id != Constants.System.Root)
{
query.Where(x => x.ParentId == id);
}
@@ -536,8 +536,8 @@ namespace Umbraco.Core.Services
{
var query = Query.Builder;
- //if the id is -1, then just get all
- if (id > 0)
+ //if the id is System Root, then just get all
+ if (id != Constants.System.Root)
{
query.Where(x => x.Path.SqlContains(string.Format(",{0},", id), TextColumnType.NVarchar));
}
@@ -614,7 +614,7 @@ namespace Umbraco.Core.Services
/// Parent object
public IContent GetParent(IContent content)
{
- if (content.ParentId == -1 || content.ParentId == -20)
+ if (content.ParentId == Constants.System.Root || content.ParentId == Constants.System.RecycleBinContent)
return null;
return GetById(content.ParentId);
@@ -639,7 +639,7 @@ namespace Umbraco.Core.Services
{
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
- var query = Query.Builder.Where(x => x.ParentId == -1);
+ var query = Query.Builder.Where(x => x.ParentId == Constants.System.Root);
var contents = repository.GetByQuery(query);
return contents;
@@ -697,7 +697,7 @@ namespace Umbraco.Core.Services
{
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
- var query = Query.Builder.Where(x => x.Path.Contains("-20"));
+ var query = Query.Builder.Where(x => x.Path.Contains(Constants.System.RecycleBinContent.ToInvariantString()));
var contents = repository.GetByQuery(query);
return contents;
@@ -960,7 +960,7 @@ namespace Umbraco.Core.Services
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs(asArray, false), this);
- Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, -1);
+ Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, Constants.System.Root);
}
}
@@ -1004,7 +1004,7 @@ namespace Umbraco.Core.Services
Audit.Add(AuditTypes.Delete,
string.Format("Delete Content of Type {0} performed by user", contentTypeId),
- userId, -1);
+ userId, Constants.System.Root);
}
}
@@ -1075,7 +1075,7 @@ namespace Umbraco.Core.Services
DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate), this);
- Audit.Add(AuditTypes.Delete, "Delete Content by version date performed by user", userId, -1);
+ Audit.Add(AuditTypes.Delete, "Delete Content by version date performed by user", userId, Constants.System.Root);
}
///
@@ -1108,7 +1108,7 @@ namespace Umbraco.Core.Services
DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, specificVersion: versionId), this);
- Audit.Add(AuditTypes.Delete, "Delete Content by version performed by user", userId, -1);
+ Audit.Add(AuditTypes.Delete, "Delete Content by version performed by user", userId, Constants.System.Root);
}
}
@@ -1191,7 +1191,7 @@ namespace Umbraco.Core.Services
using (new WriteLock(Locker))
{
//This ensures that the correct method is called if this method is used to Move to recycle bin.
- if (parentId == -20)
+ if (parentId == Constants.System.RecycleBinContent)
{
MoveToRecycleBin(content, userId);
return;
@@ -1251,7 +1251,7 @@ namespace Umbraco.Core.Services
}
- Audit.Add(AuditTypes.Delete, "Empty Content Recycle Bin performed by user", 0, -20);
+ Audit.Add(AuditTypes.Delete, "Empty Content Recycle Bin performed by user", 0, Constants.System.RecycleBinContent);
}
///
@@ -1486,7 +1486,7 @@ namespace Umbraco.Core.Services
contentTypeIds: contentTypeIds.Length == 0 ? null : contentTypeIds);
}
- Audit.Add(AuditTypes.Publish, "ContentService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
+ Audit.Add(AuditTypes.Publish, "ContentService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, Constants.System.Root);
}
@@ -1518,9 +1518,9 @@ namespace Umbraco.Core.Services
moveInfo.Add(new MoveEventInfo(content, content.Path, parentId));
content.WriterId = userId;
- if (parentId == -1)
+ if (parentId == Constants.System.Root)
{
- content.Path = string.Concat("-1,", content.Id);
+ content.Path = string.Concat(Constants.System.Root, ",", content.Id);
content.Level = 1;
}
else
@@ -1609,7 +1609,7 @@ namespace Umbraco.Core.Services
var result = new List>();
//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)
+ if (content.ParentId != Constants.System.Root && content.ParentId != Constants.System.RecycleBinContent && IsPublishable(content) == false)
{
LogHelper.Info(
string.Format(
@@ -1862,11 +1862,11 @@ namespace Umbraco.Core.Services
foreach (var id in ids)
{
//If Id equals that of the recycle bin we return false because nothing in the bin can be published
- if (id == -20)
+ if (id == Constants.System.RecycleBinContent)
return false;
//We don't check the System Root, so just continue
- if (id == -1) continue;
+ if (id == Constants.System.Root) continue;
//If the current id equals that of the passed in content and if current shouldn't be checked we skip it.
if (checkCurrent == false && id == content.Id) continue;
@@ -1883,7 +1883,7 @@ namespace Umbraco.Core.Services
private PublishStatusType CheckAndLogIsPublishable(IContent content)
{
//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)
+ if (content.ParentId != Constants.System.Root && content.ParentId != Constants.System.RecycleBinContent && IsPublishable(content) == false)
{
LogHelper.Info(
string.Format(
diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs
index b87dfa1ac1..4c1f9cd038 100644
--- a/src/Umbraco.Core/Services/ContentTypeService.cs
+++ b/src/Umbraco.Core/Services/ContentTypeService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
+using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml.Linq;
@@ -8,11 +9,13 @@ using System.Threading;
using Umbraco.Core.Auditing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
+using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
+using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
@@ -273,6 +276,74 @@ namespace Umbraco.Core.Services
}
+ public void Validate(IContentTypeComposition compo)
+ {
+ using (new WriteLock(Locker))
+ {
+ ValidateLocked(compo);
+ }
+ }
+
+ private void ValidateLocked(IContentTypeComposition compositionContentType)
+ {
+ // performs business-level validation of the composition
+ // should ensure that it is absolutely safe to save the composition
+
+ // eg maybe a property has been added, with an alias that's OK (no conflict with ancestors)
+ // but that cannot be used (conflict with descendants)
+
+ var contentType = compositionContentType as IContentType;
+ var mediaType = compositionContentType as IMediaType;
+
+ IContentTypeComposition[] allContentTypes;
+ if (contentType != null)
+ allContentTypes = GetAllContentTypes().Cast().ToArray();
+ else if (mediaType != null)
+ allContentTypes = GetAllMediaTypes().Cast().ToArray();
+ else
+ throw new Exception("Composition is neither IContentType nor IMediaType?");
+
+ var compositionAliases = compositionContentType.CompositionAliases();
+ var compositions = allContentTypes.Where(x => compositionAliases.Any(y => x.Alias.Equals(y)));
+ var propertyTypeAliases = compositionContentType.PropertyTypes.Select(x => x.Alias.ToLowerInvariant()).ToArray();
+ var indirectReferences = allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == compositionContentType.Id));
+ var comparer = new DelegateEqualityComparer((x, y) => x.Id == y.Id, x => x.Id);
+ var dependencies = new HashSet(compositions, comparer);
+ var stack = new Stack();
+ indirectReferences.ForEach(stack.Push);//Push indirect references to a stack, so we can add recursively
+ while (stack.Count > 0)
+ {
+ var indirectReference = stack.Pop();
+ dependencies.Add(indirectReference);
+ //Get all compositions for the current indirect reference
+ var directReferences = indirectReference.ContentTypeComposition;
+
+ foreach (var directReference in directReferences)
+ {
+ if (directReference.Id == compositionContentType.Id || directReference.Alias.Equals(compositionContentType.Alias)) continue;
+ dependencies.Add(directReference);
+ //A direct reference has compositions of its own - these also need to be taken into account
+ var directReferenceGraph = directReference.CompositionAliases();
+ allContentTypes.Where(x => directReferenceGraph.Any(y => x.Alias.Equals(y, StringComparison.InvariantCultureIgnoreCase))).ForEach(c => dependencies.Add(c));
+ }
+ //Recursive lookup of indirect references
+ allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == indirectReference.Id)).ForEach(stack.Push);
+ }
+
+ foreach (var dependency in dependencies)
+ {
+ if (dependency.Id == compositionContentType.Id) continue;
+ var contentTypeDependency = allContentTypes.FirstOrDefault(x => x.Alias.Equals(dependency.Alias, StringComparison.InvariantCultureIgnoreCase));
+ if (contentTypeDependency == null) continue;
+ var intersect = contentTypeDependency.PropertyTypes.Select(x => x.Alias.ToLowerInvariant()).Intersect(propertyTypeAliases).ToArray();
+ if (intersect.Length == 0) continue;
+
+ var message = string.Format("The following PropertyType aliases from the current ContentType conflict with existing PropertyType aliases: {0}.",
+ string.Join(", ", intersect));
+ throw new Exception(message);
+ }
+ }
+
///
/// Saves a single object
///
@@ -288,6 +359,7 @@ namespace Umbraco.Core.Services
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentTypeRepository(uow))
{
+ ValidateLocked(contentType); // throws if invalid
contentType.CreatorId = userId;
repository.AddOrUpdate(contentType);
@@ -317,6 +389,11 @@ namespace Umbraco.Core.Services
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentTypeRepository(uow))
{
+ // all-or-nothing, validate them all first
+ foreach (var contentType in asArray)
+ {
+ ValidateLocked(contentType); // throws if invalid
+ }
foreach (var contentType in asArray)
{
contentType.CreatorId = userId;
@@ -487,6 +564,7 @@ namespace Umbraco.Core.Services
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaTypeRepository(uow))
{
+ ValidateLocked(mediaType); // throws if invalid
mediaType.CreatorId = userId;
repository.AddOrUpdate(mediaType);
uow.Commit();
@@ -517,7 +595,11 @@ namespace Umbraco.Core.Services
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaTypeRepository(uow))
{
-
+ // all-or-nothing, validate them all first
+ foreach (var mediaType in asArray)
+ {
+ ValidateLocked(mediaType); // throws if invalid
+ }
foreach (var mediaType in asArray)
{
mediaType.CreatorId = userId;
diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs
index 39e7b63e99..63e0a390e7 100644
--- a/src/Umbraco.Core/Services/DataTypeService.cs
+++ b/src/Umbraco.Core/Services/DataTypeService.cs
@@ -200,8 +200,22 @@ namespace Umbraco.Core.Services
/// Id of the user issueing the save
public void Save(IEnumerable dataTypeDefinitions, int userId = 0)
{
- if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinitions), this))
- return;
+ Save(dataTypeDefinitions, userId, true);
+ }
+
+ ///
+ /// Saves a collection of
+ ///
+ /// to save
+ /// Id of the user issueing the save
+ /// Boolean indicating whether or not to raise events
+ public void Save(IEnumerable dataTypeDefinitions, int userId, bool raiseEvents)
+ {
+ if (raiseEvents)
+ {
+ if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinitions), this))
+ return;
+ }
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
@@ -213,7 +227,8 @@ namespace Umbraco.Core.Services
}
uow.Commit();
- Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinitions, false), this);
+ if (raiseEvents)
+ Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinitions, false), this);
}
Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1);
@@ -229,30 +244,27 @@ namespace Umbraco.Core.Services
{
//TODO: Should we raise an event here since we are really saving values for the data type?
- using (new WriteLock(Locker))
+ using (var uow = _uowProvider.GetUnitOfWork())
{
- using (var uow = _uowProvider.GetUnitOfWork())
+ using (var transaction = uow.Database.GetTransaction())
{
- using (var transaction = uow.Database.GetTransaction())
+ var sortOrderObj =
+ uow.Database.ExecuteScalar
public interface IFileService : IService
{
+ IEnumerable GetPartialViewSnippetNames(params string[] filterNames);
+ void DeletePartialViewFolder(string folderPath);
+ void DeletePartialViewMacroFolder(string folderPath);
+ IPartialView GetPartialView(string path);
+ IPartialView GetPartialViewMacro(string path);
+ Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = 0);
+ Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = 0);
+ bool DeletePartialView(string path, int userId = 0);
+ bool DeletePartialViewMacro(string path, int userId = 0);
+ Attempt SavePartialView(IPartialView partialView, int userId = 0);
+ Attempt SavePartialViewMacro(IPartialView partialView, int userId = 0);
+
///
/// Gets a list of all objects
///
diff --git a/src/Umbraco.Core/Services/ILocalizedTextService.cs b/src/Umbraco.Core/Services/ILocalizedTextService.cs
new file mode 100644
index 0000000000..de95f24efa
--- /dev/null
+++ b/src/Umbraco.Core/Services/ILocalizedTextService.cs
@@ -0,0 +1,37 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace Umbraco.Core.Services
+{
+ ///
+ /// The entry point to localize any key in the text storage source for a given culture
+ ///
+ ///
+ /// This class is created to be as simple as possible so that it can be replaced very easily,
+ /// all other methods are extension methods that simply call the one underlying method in this class
+ ///
+ public interface ILocalizedTextService
+ {
+ ///
+ /// Localize a key with variables
+ ///
+ ///
+ ///
+ /// This can be null
+ ///
+ string Localize(string key, CultureInfo culture, IDictionary tokens = null);
+
+ ///
+ /// Returns all key/values in storage for the given culture
+ ///
+ ///
+ IDictionary GetAllStoredValues(CultureInfo culture);
+
+ ///
+ /// Returns a list of all currently supported cultures
+ ///
+ ///
+ IEnumerable GetSupportedCultures();
+ }
+}
diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs
index 559bf100d0..5a77956931 100644
--- a/src/Umbraco.Core/Services/IUserService.cs
+++ b/src/Umbraco.Core/Services/IUserService.cs
@@ -53,6 +53,14 @@ namespace Umbraco.Core.Services
/// Alias of the section to remove
void DeleteSectionFromAllUsers(string sectionAlias);
+ ///
+ /// Add a specific section to all users or those specified as parameters
+ ///
+ /// This is useful when a new section is created to allow specific users accessing it
+ /// Alias of the section to add
+ /// Specifiying nothing will add the section to all user
+ void AddSectionToAllUsers(string sectionAlias, params int[] userIds);
+
///
/// Get permissions set for a user and optional node ids
///
@@ -117,4 +125,4 @@ namespace Umbraco.Core.Services
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Services/LocalizedTextService.cs b/src/Umbraco.Core/Services/LocalizedTextService.cs
new file mode 100644
index 0000000000..c9d05f3a3b
--- /dev/null
+++ b/src/Umbraco.Core/Services/LocalizedTextService.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using Umbraco.Core.Logging;
+
+namespace Umbraco.Core.Services
+{
+ //TODO: Convert all of this over to Niels K's localization framework one day
+
+ public class LocalizedTextService : ILocalizedTextService
+ {
+ private readonly IDictionary>> _dictionarySource;
+ private readonly IDictionary> _xmlSource;
+
+ ///
+ /// Initializes with a file sources instance
+ ///
+ ///
+ public LocalizedTextService(LocalizedTextServiceFileSources fileSources)
+ {
+ _xmlSource = fileSources.GetXmlSources();
+ }
+
+ ///
+ /// Initializes with an XML source
+ ///
+ ///
+ public LocalizedTextService(IDictionary> source)
+ {
+ if (source == null) throw new ArgumentNullException("source");
+ _xmlSource = source;
+ }
+
+ ///
+ /// Initializes with a source of a dictionary of culture -> areas -> sub dictionary of keys/values
+ ///
+ ///
+ public LocalizedTextService(IDictionary>> source)
+ {
+ if (source == null) throw new ArgumentNullException("source");
+ _dictionarySource = source;
+ }
+
+ public string Localize(string key, CultureInfo culture, IDictionary tokens = null)
+ {
+ Mandate.ParameterNotNull(culture, "culture");
+
+ //This is what the legacy ui service did
+ if (string.IsNullOrEmpty(key))
+ return string.Empty;
+
+ var keyParts = key.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
+ var area = keyParts.Length > 1 ? keyParts[0] : null;
+ var alias = keyParts.Length > 1 ? keyParts[1] : keyParts[0];
+
+ if (_xmlSource != null)
+ {
+ return GetFromXmlSource(culture, area, alias, tokens);
+ }
+ else
+ {
+ return GetFromDictionarySource(culture, area, alias, tokens);
+ }
+ }
+
+ ///
+ /// Returns all key/values in storage for the given culture
+ ///
+ ///
+ public IDictionary GetAllStoredValues(CultureInfo culture)
+ {
+ if (culture == null) throw new ArgumentNullException("culture");
+
+ var result = new Dictionary();
+
+ if (_xmlSource != null)
+ {
+ if (_xmlSource.ContainsKey(culture) == false)
+ {
+ LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture);
+ return result;
+ }
+
+ //convert all areas + keys to a single key with a '/'
+ var areas = _xmlSource[culture].Value.XPathSelectElements("//area");
+ foreach (var area in areas)
+ {
+ var keys = area.XPathSelectElements("./key");
+ foreach (var key in keys)
+ {
+ var dictionaryKey = string.Format("{0}/{1}", (string) area.Attribute("alias"), (string) key.Attribute("alias"));
+ //there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files
+ if (result.ContainsKey(dictionaryKey) == false)
+ {
+ result.Add(dictionaryKey, key.Value);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (_dictionarySource.ContainsKey(culture) == false)
+ {
+ LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture);
+ return result;
+ }
+
+ //convert all areas + keys to a single key with a '/'
+ foreach (var area in _dictionarySource[culture])
+ {
+ foreach (var key in area.Value)
+ {
+ var dictionaryKey = string.Format("{0}/{1}", area.Key, key.Key);
+ //i don't think it's possible to have duplicates because we're dealing with a dictionary in the first place, but we'll double check here just in case.
+ if (result.ContainsKey(dictionaryKey) == false)
+ {
+ result.Add(dictionaryKey, key.Value);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Returns a list of all currently supported cultures
+ ///
+ ///
+ public IEnumerable GetSupportedCultures()
+ {
+ return _xmlSource != null ? _xmlSource.Keys : _dictionarySource.Keys;
+ }
+
+ private string GetFromDictionarySource(CultureInfo culture, string area, string key, IDictionary tokens)
+ {
+ if (_dictionarySource.ContainsKey(culture) == false)
+ {
+ LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture);
+ return "[" + key + "]";
+ }
+
+ var cultureSource = _dictionarySource[culture];
+
+ string found;
+ if (area.IsNullOrWhiteSpace())
+ {
+ found = cultureSource
+ .SelectMany(x => x.Value)
+ .Where(keyvals => keyvals.Key.InvariantEquals(key))
+ .Select(x => x.Value)
+ .FirstOrDefault();
+ }
+ else
+ {
+ found = cultureSource
+ .Where(areas => areas.Key.InvariantEquals(area))
+ .SelectMany(a => a.Value)
+ .Where(keyvals => keyvals.Key.InvariantEquals(key))
+ .Select(x => x.Value)
+ .FirstOrDefault();
+ }
+
+ if (found != null)
+ {
+ return ParseTokens(found, tokens);
+ }
+
+ //NOTE: Based on how legacy works, the default text does not contain the area, just the key
+ return "[" + key + "]";
+ }
+
+ private string GetFromXmlSource(CultureInfo culture, string area, string key, IDictionary tokens)
+ {
+ if (_xmlSource.ContainsKey(culture) == false)
+ {
+ LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture);
+ return "[" + key + "]";
+ }
+
+ var cultureSource = _xmlSource[culture].Value;
+
+ var xpath = area.IsNullOrWhiteSpace()
+ ? string.Format("//key [@alias = '{0}']", key)
+ : string.Format("//area [@alias = '{0}']/key [@alias = '{1}']", area, key);
+
+ var found = cultureSource.XPathSelectElement(xpath);
+
+ if (found != null)
+ {
+ return ParseTokens(found.Value, tokens);
+ }
+
+ //NOTE: Based on how legacy works, the default text does not contain the area, just the key
+ return "[" + key + "]";
+ }
+
+ ///
+ /// Parses the tokens in the value
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// This is based on how the legacy ui localized text worked, each token was just a sequential value delimited with a % symbol.
+ /// For example: hello %0%, you are %1% !
+ ///
+ /// Since we're going to continue using the same language files for now, the token system needs to remain the same. With our new service
+ /// we support a dictionary which means in the future we can really have any sort of token system.
+ /// Currently though, the token key's will need to be an integer and sequential - though we aren't going to throw exceptions if that is not the case.
+ ///
+ internal string ParseTokens(string value, IDictionary tokens)
+ {
+ if (tokens == null || tokens.Any() == false)
+ {
+ return value;
+ }
+
+ foreach (var token in tokens)
+ {
+ value = value.Replace(string.Format("{0}{1}{0}", "%", token.Key), token.Value);
+ }
+
+ return value;
+ }
+
+ }
+}
diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs
new file mode 100644
index 0000000000..6ed20b2a45
--- /dev/null
+++ b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+namespace Umbraco.Core.Services
+{
+
+ ///
+ /// Extension methods for ILocalizedTextService
+ ///
+ public static class LocalizedTextServiceExtensions
+ {
+ ///
+ /// Localize a key without any variables
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string Localize(this ILocalizedTextService manager, string key, CultureInfo culture, string[] tokens)
+ {
+ return manager.Localize(key, culture, ConvertToDictionaryVars(tokens));
+ }
+
+ ///
+ /// Convert an array of strings to a dictionary of indicies -> values
+ ///
+ ///
+ ///
+ internal static IDictionary ConvertToDictionaryVars(string[] variables)
+ {
+ if (variables == null) return null;
+ if (variables.Any() == false) return null;
+
+ return variables.Select((s, i) => new { index = i.ToString(CultureInfo.InvariantCulture), value = s })
+ .ToDictionary(keyvals => keyvals.index, keyvals => keyvals.value);
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs b/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs
new file mode 100644
index 0000000000..30e43a694c
--- /dev/null
+++ b/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Xml.Linq;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Logging;
+
+namespace Umbraco.Core.Services
+{
+ ///
+ /// Exposes the XDocument sources from files for the default localization text service and ensure caching is taken care of
+ ///
+ public class LocalizedTextServiceFileSources
+ {
+ private readonly IRuntimeCacheProvider _cache;
+ private readonly DirectoryInfo _fileSourceFolder;
+
+ public LocalizedTextServiceFileSources(IRuntimeCacheProvider cache, DirectoryInfo fileSourceFolder)
+ {
+ if (cache == null) throw new ArgumentNullException("cache");
+ if (fileSourceFolder == null) throw new ArgumentNullException("fileSourceFolder");
+ _cache = cache;
+
+ if (fileSourceFolder.Exists == false)
+ {
+ LogHelper.Warn("The folder does not exist: {0}, therefore no sources will be discovered", () => fileSourceFolder.FullName);
+ }
+ else
+ {
+ _fileSourceFolder = fileSourceFolder;
+ }
+ }
+
+ ///
+ /// returns all xml sources for all culture files found in the folder
+ ///
+ ///
+ public IDictionary> GetXmlSources()
+ {
+ var result = new Dictionary>();
+
+ if (_fileSourceFolder == null) return result;
+
+ foreach (var fileInfo in _fileSourceFolder.GetFiles("*.xml"))
+ {
+ var localCopy = fileInfo;
+ var filename = Path.GetFileNameWithoutExtension(localCopy.FullName).Replace("_", "-");
+ var culture = CultureInfo.GetCultureInfo(filename);
+ //get the lazy value from cache
+ result.Add(culture, new Lazy(() => _cache.GetCacheItem(
+ string.Format("{0}-{1}", typeof (LocalizedTextServiceFileSources).Name, culture.TwoLetterISOLanguageName), () =>
+ {
+ using (var fs = localCopy.OpenRead())
+ {
+ return XDocument.Load(fs);
+ }
+ }, isSliding: true, timeout: TimeSpan.FromMinutes(10), dependentFiles: new[] {localCopy.FullName})));
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/MacroService.cs b/src/Umbraco.Core/Services/MacroService.cs
index 376d33503b..9e7dc66b59 100644
--- a/src/Umbraco.Core/Services/MacroService.cs
+++ b/src/Umbraco.Core/Services/MacroService.cs
@@ -200,5 +200,7 @@ namespace Umbraco.Core.Services
///
public static event TypedEventHandler> Saved;
#endregion
+
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs
index f2c5526e8d..279ec9f07f 100644
--- a/src/Umbraco.Core/Services/MediaService.cs
+++ b/src/Umbraco.Core/Services/MediaService.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;
using Umbraco.Core.Auditing;
@@ -413,7 +414,7 @@ namespace Umbraco.Core.Services
{
var query = Query.Builder;
//if the id is -1, then just get all
- if (id > 0)
+ if (id != -1)
{
query.Where(x => x.ParentId == id);
}
@@ -443,7 +444,7 @@ namespace Umbraco.Core.Services
var query = Query.Builder;
//if the id is -1, then just get all
- if (id > 0)
+ if (id != -1)
{
query.Where(x => x.Path.SqlContains(string.Format(",{0},", id), TextColumnType.NVarchar));
}
@@ -567,28 +568,38 @@ namespace Umbraco.Core.Services
public IMedia GetMediaByPath(string mediaPath)
{
var umbracoFileValue = mediaPath;
- var isResized = mediaPath.Contains("_") && mediaPath.Contains("x");
+ const string Pattern = ".*[_][0-9]+[x][0-9]+[.].*";
+ var isResized = Regex.IsMatch(mediaPath, Pattern);
// If the image has been resized we strip the "_403x328" of the original "/media/1024/koala_403x328.jpg" url.
if (isResized)
{
-
var underscoreIndex = mediaPath.LastIndexOf('_');
var dotIndex = mediaPath.LastIndexOf('.');
umbracoFileValue = string.Concat(mediaPath.Substring(0, underscoreIndex), mediaPath.Substring(dotIndex));
}
- var sql = new Sql()
- .Select("*")
- .From()
- .InnerJoin()
- .On(left => left.PropertyTypeId, right => right.Id)
- .Where(x => x.Alias == "umbracoFile")
- .Where(x => x.VarChar == umbracoFileValue);
+ Func createSql = url => new Sql().Select("*")
+ .From()
+ .InnerJoin()
+ .On(left => left.PropertyTypeId, right => right.Id)
+ .Where(x => x.Alias == "umbracoFile")
+ .Where(x => x.VarChar == url);
+
+ var sql = createSql(umbracoFileValue);
using (var uow = _uowProvider.GetUnitOfWork())
{
var propertyDataDto = uow.Database.Fetch(sql).FirstOrDefault();
+
+ // If the stripped-down url returns null, we try again with the original url.
+ // Previously, the function would fail on e.g. "my_x_image.jpg"
+ if (propertyDataDto == null)
+ {
+ sql = createSql(mediaPath);
+ propertyDataDto = uow.Database.Fetch(sql).FirstOrDefault();
+ }
+
return propertyDataDto == null ? null : GetById(propertyDataDto.NodeId);
}
}
diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs
index 64b5f7629b..579da33a9d 100644
--- a/src/Umbraco.Core/Services/PackagingService.cs
+++ b/src/Umbraco.Core/Services/PackagingService.cs
@@ -76,7 +76,7 @@ namespace Umbraco.Core.Services
public XElement Export(IContent content, bool deep = false, bool raiseEvents = true)
{
var nodeName = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "node" : content.ContentType.Alias.ToSafeAliasWithForcingCheck();
-
+
if (raiseEvents)
{
if (ExportingContent.IsRaisedEventCancelled(new ExportEventArgs(content, nodeName), this))
@@ -86,13 +86,13 @@ namespace Umbraco.Core.Services
var exporter = new EntityXmlSerializer();
var xml = exporter.Serialize(_contentService, _dataTypeService, _userService, content, deep);
- if(raiseEvents)
+ if (raiseEvents)
ExportedContent.RaiseEvent(new ExportEventArgs(content, xml, false), this);
return xml;
}
-
+
///
/// Imports and saves package xml as
@@ -109,7 +109,7 @@ namespace Umbraco.Core.Services
if (ImportingContent.IsRaisedEventCancelled(new ImportEventArgs(element), this))
return Enumerable.Empty();
}
-
+
var name = element.Name.LocalName;
if (name.Equals("DocumentSet"))
{
@@ -122,7 +122,7 @@ namespace Umbraco.Core.Services
if (contents.Any())
_contentService.Save(contents, userId);
- if(raiseEvents)
+ if (raiseEvents)
ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this);
return contents;
}
@@ -136,7 +136,7 @@ namespace Umbraco.Core.Services
if (contents.Any())
_contentService.Save(contents, userId);
- if(raiseEvents)
+ if (raiseEvents)
ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this);
return contents;
}
@@ -250,7 +250,7 @@ namespace Umbraco.Core.Services
//TODO: We need to refactor this so the packager isn't making direct db calls for an 'edge' case
var database = ApplicationContext.Current.DatabaseContext.Database;
- var dtos = database.Fetch("WHERE datatypeNodeId = @Id", new {Id = propertyType.DataTypeDefinitionId});
+ var dtos = database.Fetch("WHERE datatypeNodeId = @Id", new { Id = propertyType.DataTypeDefinitionId });
var propertyValueList = new List();
foreach (var preValue in propertyValue.Split(','))
@@ -332,11 +332,62 @@ namespace Umbraco.Core.Services
}
_importedContentTypes = new Dictionary();
- var documentTypes = name.Equals("DocumentTypes")
+ var unsortedDocumentTypes = name.Equals("DocumentTypes")
? (from doc in element.Elements("DocumentType") select doc).ToList()
: new List { element };
- //NOTE it might be an idea to sort the doctype XElements based on dependencies
- //before creating the doc types - should also allow for a better structure/inheritance support.
+
+ //When you are importing a single doc type we have to assume that the depedencies are already there.
+ //Otherwise something like uSync won't work.
+ var fields = new List>();
+ var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1;
+ if (isSingleDocTypeImport == false)
+ {
+ //NOTE Here we sort the doctype XElements based on dependencies
+ //before creating the doc types - this should also allow for a better structure/inheritance support.
+ foreach (var documentType in unsortedDocumentTypes)
+ {
+ var elementCopy = documentType;
+ var infoElement = elementCopy.Element("Info");
+ var dependencies = new List();
+
+ //Add the Master as a dependency
+ if (elementCopy.Element("Master") != null &&
+ string.IsNullOrEmpty(elementCopy.Element("Master").Value) == false)
+ {
+ dependencies.Add(elementCopy.Element("Master").Value);
+ }
+
+ //Add compositions as dependencies
+ var compositionsElement = infoElement.Element("Compositions");
+ if (compositionsElement != null && compositionsElement.HasElements)
+ {
+ var compositions = compositionsElement.Elements("Composition");
+ if (compositions.Any())
+ {
+ foreach (var composition in compositions)
+ {
+ dependencies.Add(composition.Value);
+ }
+ }
+ }
+
+ var field = new TopologicalSorter.DependencyField
+ {
+ Alias = infoElement.Element("Alias").Value,
+ Item = new Lazy(() => elementCopy),
+ DependsOn = dependencies.ToArray()
+ };
+
+ fields.Add(field);
+ }
+ }
+
+ //Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921
+ var documentTypes = isSingleDocTypeImport
+ ? unsortedDocumentTypes.ToList()
+ : TopologicalSorter.GetSortedItems(fields).ToList();
+
+ //Iterate the sorted document types and create them as IContentType objects
foreach (var documentType in documentTypes)
{
var alias = documentType.Element("Info").Element("Alias").Value;
@@ -349,9 +400,12 @@ namespace Umbraco.Core.Services
}
}
+ //Save the newly created/updated IContentType objects
var list = _importedContentTypes.Select(x => x.Value).ToList();
_contentTypeService.Save(list, userId);
+ //Now we can finish the import by updating the 'structure',
+ //which requires the doc types to be saved/available in the db
if (importStructure)
{
var updatedContentTypes = new List();
@@ -392,15 +446,10 @@ namespace Umbraco.Core.Services
: _contentTypeService.GetContentType(masterAlias);
}
+ var alias = infoElement.Element("Alias").Value;
var contentType = parent == null
- ? new ContentType(-1)
- {
- Alias = infoElement.Element("Alias").Value
- }
- : new ContentType(parent)
- {
- Alias = infoElement.Element("Alias").Value
- };
+ ? new ContentType(-1) { Alias = alias }
+ : new ContentType(parent, alias);
if (parent != null)
contentType.AddContentType(parent);
@@ -417,15 +466,47 @@ namespace Umbraco.Core.Services
contentType.Icon = infoElement.Element("Icon").Value;
contentType.Thumbnail = infoElement.Element("Thumbnail").Value;
contentType.Description = infoElement.Element("Description").Value;
+
//NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it.
var allowAtRoot = infoElement.Element("AllowAtRoot");
if (allowAtRoot != null)
contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true");
+
//NOTE IsListView is a new property in the package xml so we need to verify it exists before using it.
var isListView = infoElement.Element("IsListView");
if (isListView != null)
contentType.IsContainer = isListView.Value.InvariantEquals("true");
+ //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set
+ var masterElement = infoElement.Element("Master");
+ if (masterElement != null)
+ {
+ var masterAlias = masterElement.Value;
+ IContentType parent = _importedContentTypes.ContainsKey(masterAlias)
+ ? _importedContentTypes[masterAlias]
+ : _contentTypeService.GetContentType(masterAlias);
+
+ contentType.SetLazyParentId(new Lazy(() => parent.Id));
+ }
+
+ //Update Compositions on the ContentType to ensure that they are as is defined in the package xml
+ var compositionsElement = infoElement.Element("Compositions");
+ if (compositionsElement != null && compositionsElement.HasElements)
+ {
+ var compositions = compositionsElement.Elements("Composition");
+ if (compositions.Any())
+ {
+ foreach (var composition in compositions)
+ {
+ var compositionAlias = composition.Value;
+ var compositionContentType = _importedContentTypes.ContainsKey(compositionAlias)
+ ? _importedContentTypes[compositionAlias]
+ : _contentTypeService.GetContentType(compositionAlias);
+ var added = contentType.AddContentType(compositionContentType);
+ }
+ }
+ }
+
UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement);
UpdateContentTypesTabs(contentType, documentType.Element("Tabs"));
UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties"));
@@ -492,13 +573,13 @@ namespace Umbraco.Core.Services
{
contentType.AddPropertyGroup(caption);
- int sortOrder;
- if(tab.Element("SortOrder") != null &&
- int.TryParse(tab.Element("SortOrder").Value, out sortOrder))
- {
- // Override the sort order with the imported value
- contentType.PropertyGroups[caption].SortOrder = sortOrder;
- }
+ }
+
+ int sortOrder;
+ if (tab.Element("SortOrder") != null && int.TryParse(tab.Element("SortOrder").Value, out sortOrder))
+ {
+ // Override the sort order with the imported value
+ contentType.PropertyGroups[caption].SortOrder = sortOrder;
}
}
}
@@ -538,7 +619,7 @@ namespace Umbraco.Core.Services
dataTypeDefinition = dataTypeDefinitions.First();
}
}
-
+
// For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently.
// This means that the property will not be created.
if (dataTypeDefinition == null)
@@ -738,9 +819,15 @@ namespace Umbraco.Core.Services
var list = dataTypes.Select(x => x.Value).ToList();
if (list.Any())
{
- _dataTypeService.Save(list, userId);
+ //NOTE: As long as we have to deal with the two types of PreValue lists (with/without Keys)
+ //this is a bit of a pain to handle while ensuring that the imported DataTypes has PreValues
+ //place when triggering the save event.
- SavePrevaluesFromXml(list, dataTypeElements);
+ _dataTypeService.Save(list, userId, false);//Save without raising events
+
+ SavePrevaluesFromXml(list, dataTypeElements);//Save the PreValues for the current list of DataTypes
+
+ _dataTypeService.Save(list, userId, true);//Re-save and raise events
}
if (raiseEvents)
@@ -760,15 +847,15 @@ namespace Umbraco.Core.Services
var dataTypeDefinition = dataTypes.First(x => x.Name == dataTypeDefinitionName);
var valuesWithoutKeys = prevaluesElement.Elements("PreValue")
- .Where(x => ((string) x.Attribute("Alias")).IsNullOrWhiteSpace())
+ .Where(x => ((string)x.Attribute("Alias")).IsNullOrWhiteSpace())
.Select(x => x.Attribute("Value").Value);
var valuesWithKeys = prevaluesElement.Elements("PreValue")
- .Where(x => ((string) x.Attribute("Alias")).IsNullOrWhiteSpace() == false)
+ .Where(x => ((string)x.Attribute("Alias")).IsNullOrWhiteSpace() == false)
.ToDictionary(
- key => (string) key.Attribute("Alias"),
- val => new PreValue((string) val.Attribute("Value")));
-
+ key => (string)key.Attribute("Alias"),
+ val => new PreValue((string)val.Attribute("Value")));
+
//save the values with keys
_dataTypeService.SavePreValues(dataTypeDefinition, valuesWithKeys);
@@ -815,7 +902,7 @@ namespace Umbraco.Core.Services
var exporter = new EntityXmlSerializer();
var xml = exporter.Serialize(dictionaryItem);
-
+
if (includeChildren)
{
var children = _localizationService.GetDictionaryItemChildren(dictionaryItem.Key);
@@ -1089,7 +1176,7 @@ namespace Umbraco.Core.Services
dontRender = bool.Parse(dontRenderElement.Value);
}
- var existingMacro = _macroService.GetByAlias(macroAlias) as Macro;
+ var existingMacro = _macroService.GetByAlias(macroAlias) as Macro;
var macro = existingMacro ?? new Macro(macroAlias, macroName, controlType, controlAssembly, xsltPath, scriptPath,
cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration);
@@ -1184,7 +1271,7 @@ namespace Umbraco.Core.Services
public XElement Export(IMedia media, bool deep = false, bool raiseEvents = true)
{
var nodeName = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "node" : media.ContentType.Alias.ToSafeAliasWithForcingCheck();
-
+
if (raiseEvents)
{
if (ExportingMedia.IsRaisedEventCancelled(new ExportEventArgs(media, nodeName), this))
@@ -1194,7 +1281,7 @@ namespace Umbraco.Core.Services
var exporter = new EntityXmlSerializer();
var xml = exporter.Serialize(_mediaService, _dataTypeService, _userService, media, deep);
- if(raiseEvents)
+ if (raiseEvents)
ExportedMedia.RaiseEvent(new ExportEventArgs(media, xml, false), this);
return xml;
@@ -1308,7 +1395,7 @@ namespace Umbraco.Core.Services
if (templates.Any())
_fileService.SaveTemplate(templates, userId);
- if(raiseEvents)
+ if (raiseEvents)
ImportedTemplate.RaiseEvent(new ImportEventArgs(templates, element, false), this);
return templates;
@@ -1326,10 +1413,10 @@ namespace Umbraco.Core.Services
IEnumerable styleSheets = Enumerable.Empty();
- if(element.Elements().Any())
+ if (element.Elements().Any())
throw new NotImplementedException("This needs to be implimentet");
-
+
if (raiseEvents)
ImportingStylesheets.RaiseEvent(new ImportEventArgs(styleSheets, element, false), this);
@@ -1454,7 +1541,7 @@ namespace Umbraco.Core.Services
///
public static event TypedEventHandler> ImportedContent;
-
+
public static event TypedEventHandler> ExportingContent;
///
@@ -1581,7 +1668,7 @@ namespace Umbraco.Core.Services
/// Occurs before Importing Stylesheets
///
public static event TypedEventHandler> ImportingStylesheets;
-
+
///
/// Occurs after Template is Imported and Saved
///
@@ -1596,7 +1683,7 @@ namespace Umbraco.Core.Services
/// Occurs after Template is Exported to Xml
///
public static event TypedEventHandler> ExportedTemplate;
-
+
///
/// Occurs before Importing umbraco package
///
diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs
index 1a3568cff3..fd9519bc1c 100644
--- a/src/Umbraco.Core/Services/SectionService.cs
+++ b/src/Umbraco.Core/Services/SectionService.cs
@@ -140,6 +140,8 @@ namespace Umbraco.Core.Services
}
}
}
+
+ _isInitialized = true;
}
}
diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs
index 79280b371f..6dabf8b0b6 100644
--- a/src/Umbraco.Core/Services/ServiceContext.cs
+++ b/src/Umbraco.Core/Services/ServiceContext.cs
@@ -1,4 +1,6 @@
using System;
+using System.IO;
+using Umbraco.Core.IO;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
@@ -12,6 +14,7 @@ namespace Umbraco.Core.Services
///
public class ServiceContext
{
+ private Lazy _localizedTextService;
private Lazy _tagService;
private Lazy _contentService;
private Lazy _userService;
@@ -52,6 +55,7 @@ namespace Umbraco.Core.Services
///
///
///
+ ///
public ServiceContext(
IContentService contentService,
IMediaService mediaService,
@@ -69,8 +73,10 @@ namespace Umbraco.Core.Services
ISectionService sectionService,
IApplicationTreeService treeService,
ITagService tagService,
- INotificationService notificationService)
+ INotificationService notificationService,
+ ILocalizedTextService localizedTextService)
{
+ _localizedTextService = new Lazy(() => localizedTextService);
_tagService = new Lazy(() => tagService);
_contentService = new Lazy(() => contentService);
_mediaService = new Lazy(() => mediaService);
@@ -119,6 +125,12 @@ namespace Umbraco.Core.Services
var provider = dbUnitOfWorkProvider;
var fileProvider = fileUnitOfWorkProvider;
+
+
+ if (_localizedTextService == null)
+ _localizedTextService = new Lazy(() => new LocalizedTextService(
+ new LocalizedTextServiceFileSources(cache.RuntimeCache, new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/")))));
+
if (_notificationService == null)
_notificationService = new Lazy(() => new NotificationService(provider, _userService.Value, _contentService.Value));
@@ -174,11 +186,20 @@ namespace Umbraco.Core.Services
if (_tagService == null)
_tagService = new Lazy(() => new TagService(provider, repositoryFactory.Value));
+
if (_memberGroupService == null)
_memberGroupService = new Lazy(() => new MemberGroupService(provider, repositoryFactory.Value));
}
+ ///
+ /// Gets the
+ ///
+ public ILocalizedTextService TextService
+ {
+ get { return _localizedTextService.Value; }
+ }
+
///
/// Gets the
///
diff --git a/src/Umbraco.Core/Services/TagService.cs b/src/Umbraco.Core/Services/TagService.cs
index cf477a125b..a1339ce360 100644
--- a/src/Umbraco.Core/Services/TagService.cs
+++ b/src/Umbraco.Core/Services/TagService.cs
@@ -133,14 +133,7 @@ namespace Umbraco.Core.Services
{
using (var repository = _repositoryFactory.CreateTagRepository(_uowProvider.GetUnitOfWork()))
{
- if (tagGroup.IsNullOrWhiteSpace())
- {
- return repository.GetAll();
- }
-
- var query = Query.Builder.Where(x => x.Group == tagGroup);
- var definitions = repository.GetByQuery(query);
- return definitions;
+ return repository.GetTagsForEntityType(TaggableObjectTypes.All, tagGroup);
}
}
diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs
index 1a523835c8..9be1ce0d11 100644
--- a/src/Umbraco.Core/Services/UserService.cs
+++ b/src/Umbraco.Core/Services/UserService.cs
@@ -672,6 +672,36 @@ namespace Umbraco.Core.Services
uow.Commit();
}
}
+
+ ///
+ /// Add a specific section to all users or those specified as parameters
+ ///
+ /// This is useful when a new section is created to allow specific users accessing it
+ /// Alias of the section to add
+ /// Specifiying nothing will add the section to all user
+ public void AddSectionToAllUsers(string sectionAlias, params int[] userIds)
+ {
+ var uow = _uowProvider.GetUnitOfWork();
+ using (var repository = _repositoryFactory.CreateUserRepository(uow))
+ {
+ IEnumerable users;
+ if (userIds.Any())
+ {
+ users = repository.GetAll(userIds);
+ }
+ else
+ {
+ users = repository.GetAll();
+ }
+ foreach (var user in users.Where(u => !u.AllowedSections.InvariantContains(sectionAlias)))
+ {
+ //now add the section for each user and commit
+ user.AddAllowedSection(sectionAlias);
+ repository.AddOrUpdate(user);
+ }
+ uow.Commit();
+ }
+ }
///
/// Get permissions set for a user and optional node ids
@@ -745,4 +775,4 @@ namespace Umbraco.Core.Services
///
public static event TypedEventHandler> DeletedUserType;
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Strategies/RelateOnCopyHandler.cs b/src/Umbraco.Core/Strategies/RelateOnCopyHandler.cs
new file mode 100644
index 0000000000..e8c1956f2d
--- /dev/null
+++ b/src/Umbraco.Core/Strategies/RelateOnCopyHandler.cs
@@ -0,0 +1,42 @@
+using System;
+using Umbraco.Core.Auditing;
+using Umbraco.Core.Models;
+using Umbraco.Core.Services;
+
+namespace Umbraco.Core.Strategies
+{
+ public sealed class RelateOnCopyHandler : ApplicationEventHandler
+ {
+ protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
+ {
+ ContentService.Copied += ContentServiceCopied;
+ }
+
+ private void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs e)
+ {
+ if (e.RelateToOriginal)
+ {
+ var relationService = ApplicationContext.Current.Services.RelationService;
+
+ var relationType = relationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias);
+
+ if (relationType == null)
+ {
+ relationType = new RelationType(new Guid(Constants.ObjectTypes.Document),
+ new Guid(Constants.ObjectTypes.Document),
+ Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias,
+ Constants.Conventions.RelationTypes.RelateDocumentOnCopyName) { IsBidirectional = true };
+
+ relationService.Save(relationType);
+ }
+
+ var relation = new Relation(e.Original.Id, e.Copy.Id, relationType);
+ relationService.Save(relation);
+
+ Audit.Add(AuditTypes.Copy,
+ string.Format("Copied content with Id: '{0}' related to original content with Id: '{1}'",
+ e.Copy.Id, e.Original.Id), e.Copy.WriterId, e.Copy.Id);
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs b/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs
index 52480a1ca7..1d5ee7855a 100644
--- a/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs
+++ b/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Sync
server.ServerAddress,
server.ForcePortnumber.IsNullOrWhiteSpace() ? "80" : server.ForcePortnumber,
IOHelper.ResolveUrl(SystemDirectories.Umbraco).TrimStart('/'));
- }
+ }
}
// cannot be determined, return null if no config/original url, else use config/original url as base
@@ -86,7 +86,7 @@ namespace Umbraco.Core.Sync
}
var master = servers.FirstOrDefault();
-
+
if (master == null)
{
return CurrentServerEnvironmentStatus.Unknown;
@@ -105,12 +105,12 @@ namespace Umbraco.Core.Sync
}
if ((appId.IsNullOrWhiteSpace() == false && appId.Trim().InvariantEquals(HttpRuntime.AppDomainAppId))
- || (serverName.IsNullOrWhiteSpace() == false && serverName.Trim().InvariantEquals(NetworkHelper.MachineName)))
+ || (serverName.IsNullOrWhiteSpace() == false && serverName.Trim().InvariantEquals(NetworkHelper.MachineName)))
{
//match by appdid or server name!
- return CurrentServerEnvironmentStatus.Master;
+ return CurrentServerEnvironmentStatus.Master;
}
-
+
return CurrentServerEnvironmentStatus.Slave;
}
diff --git a/src/Umbraco.Core/TypeFinder.cs b/src/Umbraco.Core/TypeFinder.cs
index 2b8ebb99cc..6b257fecfe 100644
--- a/src/Umbraco.Core/TypeFinder.cs
+++ b/src/Umbraco.Core/TypeFinder.cs
@@ -15,6 +15,7 @@ using System.Web.Compilation;
using System.Web.Hosting;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
+using Umbraco.Core.Logging;
namespace Umbraco.Core
{
@@ -450,14 +451,23 @@ namespace Umbraco.Core
var allTypes = GetTypesWithFormattedException(a)
.ToArray();
- //now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes but have
- //the specified attribute
- var attributedTypes = allTypes
- .Where(t => (TypeHelper.IsNonStaticClass(t)
- && (onlyConcreteClasses == false || t.IsAbstract == false))
- //the type must have this attribute
- && t.GetCustomAttributes(attributeType, false).Any())
- .ToArray();
+ var attributedTypes = new Type[] {};
+ try
+ {
+ //now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes but have
+ //the specified attribute
+ attributedTypes = allTypes
+ .Where(t => (TypeHelper.IsNonStaticClass(t)
+ && (onlyConcreteClasses == false || t.IsAbstract == false))
+ //the type must have this attribute
+ && t.GetCustomAttributes(attributeType, false).Any())
+ .ToArray();
+ }
+ catch (TypeLoadException ex)
+ {
+ LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
+ continue;
+ }
//add the types to our list to return
foreach (var t in attributedTypes)
@@ -583,16 +593,25 @@ namespace Umbraco.Core
.Where(assignTypeFrom.IsAssignableFrom)
.ToArray();
- //now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes
- var filteredTypes = allSubTypes
- .Where(t => (TypeHelper.IsNonStaticClass(t)
- //Do not include nested private classes - since we are in full trust now this will find those too!
- && t.IsNestedPrivate == false
- && (onlyConcreteClasses == false || t.IsAbstract == false)
- //Do not include classes that are flagged to hide from the type finder
- && t.GetCustomAttribute() == null
- && additionalFilter(t)))
- .ToArray();
+ var filteredTypes = new Type[] { };
+ try
+ {
+ //now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes
+ filteredTypes = allSubTypes
+ .Where(t => (TypeHelper.IsNonStaticClass(t)
+ //Do not include nested private classes - since we are in full trust now this will find those too!
+ && t.IsNestedPrivate == false
+ && (onlyConcreteClasses == false || t.IsAbstract == false)
+ //Do not include classes that are flagged to hide from the type finder
+ && t.GetCustomAttribute() == null
+ && additionalFilter(t)))
+ .ToArray();
+ }
+ catch (TypeLoadException ex)
+ {
+ LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
+ continue;
+ }
//add the types to our list to return
foreach (var t in filteredTypes)
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 59a980aeef..e1eff16813 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -99,9 +99,9 @@
True..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll
-
+ True
- ..\packages\Microsoft.AspNet.Mvc.4.0.40804.0\lib\net40\System.Web.Mvc.dll
+ ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dllTrue
@@ -299,6 +299,9 @@
+
+
+
@@ -376,13 +379,14 @@
+
+
-
@@ -1046,6 +1050,8 @@
+
+
diff --git a/src/Umbraco.Core/app.config b/src/Umbraco.Core/app.config
index 53f3b4c80b..8f828418f3 100644
--- a/src/Umbraco.Core/app.config
+++ b/src/Umbraco.Core/app.config
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config
index f803d1ac3e..f01b9e68ba 100644
--- a/src/Umbraco.Core/packages.config
+++ b/src/Umbraco.Core/packages.config
@@ -3,7 +3,7 @@
-
+
diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config
index 45a43631f7..3d35ae5c93 100644
--- a/src/Umbraco.Tests/App.config
+++ b/src/Umbraco.Tests/App.config
@@ -100,7 +100,7 @@
-
+
diff --git a/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs b/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs
index c86ee01100..43e5d0fcf9 100644
--- a/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs
+++ b/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs
@@ -7,7 +7,7 @@ using Umbraco.Core.ObjectResolution;
namespace Umbraco.Tests.CoreStrings
{
- [TestFixture]
+ [TestFixture]
public class StringExtensionsTests
{
[SetUp]
diff --git a/src/Umbraco.Tests/CoreStrings/StringValidationTests.cs b/src/Umbraco.Tests/CoreStrings/StringValidationTests.cs
new file mode 100644
index 0000000000..c1f8b92438
--- /dev/null
+++ b/src/Umbraco.Tests/CoreStrings/StringValidationTests.cs
@@ -0,0 +1,28 @@
+using System.ComponentModel.DataAnnotations;
+using NUnit.Framework;
+
+namespace Umbraco.Tests.CoreStrings
+{
+ [TestFixture]
+ public class StringValidationTests
+ {
+ [Test]
+ public void Validate_Email_Address()
+ {
+ var foo = new EmailAddressAttribute();
+
+ Assert.IsTrue(foo.IsValid("someone@somewhere.com"));
+ Assert.IsTrue(foo.IsValid("someone@somewhere.co.uk"));
+ Assert.IsTrue(foo.IsValid("someone+tag@somewhere.net"));
+ Assert.IsTrue(foo.IsValid("futureTLD@somewhere.fooo"));
+
+ Assert.IsTrue(foo.IsValid("abc@xyz.financial"));
+
+ Assert.IsFalse(foo.IsValid("fdsa"));
+ Assert.IsFalse(foo.IsValid("fdsa@"));
+ Assert.IsFalse(foo.IsValid("fdsa@fdsa"));
+ Assert.IsFalse(foo.IsValid("fdsa@fdsa."));
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs b/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs
new file mode 100644
index 0000000000..7bde2ffddd
--- /dev/null
+++ b/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using Umbraco.Web;
+
+namespace Umbraco.Tests.FrontEnd
+{
+ [TestFixture]
+ public class UmbracoHelperTests
+ {
+ [Test]
+ public void Truncate_Simple()
+ {
+ var text = "Hello world, this is some text with a link";
+
+ var helper = new UmbracoHelper();
+
+ var result = helper.Truncate(text, 25).ToString();
+
+ Assert.AreEqual("Hello world, this is some…", result);
+ }
+
+ [Test]
+ public void Truncate_Inside_Word()
+ {
+ var text = "Hello world, this is some text with a link";
+
+ var helper = new UmbracoHelper();
+
+ var result = helper.Truncate(text, 24).ToString();
+
+ Assert.AreEqual("Hello world, this is som…", result);
+ }
+
+ [Test]
+ public void Truncate_With_Tag()
+ {
+ var text = "Hello world, this is some text with a link";
+
+ var helper = new UmbracoHelper();
+
+ var result = helper.Truncate(text, 35).ToString();
+
+ Assert.AreEqual("Hello world, this is some text with…", result);
+ }
+
+ }
+}
diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs
index 30fedf2465..95c324fd49 100644
--- a/src/Umbraco.Tests/MockTests.cs
+++ b/src/Umbraco.Tests/MockTests.cs
@@ -57,7 +57,8 @@ namespace Umbraco.Tests
new Mock().Object,
new Mock().Object,
new Mock().Object,
- new Mock().Object);
+ new Mock().Object,
+ Mock.Of());
Assert.Pass();
}
@@ -103,7 +104,8 @@ namespace Umbraco.Tests
new Mock().Object,
new Mock().Object,
new Mock().Object,
- new Mock().Object),
+ new Mock().Object,
+ Mock.Of()),
CacheHelper.CreateDisabledCacheHelper());
Assert.Pass();
diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs
index e687a74d1b..dfaaae1d27 100644
--- a/src/Umbraco.Tests/Models/ContentTests.cs
+++ b/src/Umbraco.Tests/Models/ContentTests.cs
@@ -6,6 +6,7 @@ using System.Web;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
+using Umbraco.Core.Exceptions;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Caching;
@@ -418,7 +419,7 @@ namespace Umbraco.Tests.Models
Assert.That(content.Properties["title"], Is.Not.Null);
Assert.That(content.Properties["title"].Alias, Is.EqualTo("title"));
Assert.That(content.Properties["title"].Value, Is.EqualTo("This is the new title"));
- Assert.That(content.Properties["metaDescription"].Value, Is.EqualTo("This is the meta description for a textpage"));
+ Assert.That(content.Properties["description"].Value, Is.EqualTo("This is the meta description for a textpage"));
}
[Test]
@@ -612,9 +613,9 @@ namespace Umbraco.Tests.Models
// Assert
Assert.That(content.Properties.Contains("author"), Is.True);
Assert.That(content.Properties.Contains("keywords"), Is.True);
- Assert.That(content.Properties.Contains("metaDescription"), Is.True);
+ Assert.That(content.Properties.Contains("description"), Is.True);
Assert.That(content.Properties["keywords"].Value, Is.EqualTo("text,page,meta"));
- Assert.That(content.Properties["metaDescription"].Value, Is.EqualTo("This is the meta description for a textpage"));
+ Assert.That(content.Properties["description"].Value, Is.EqualTo("This is the meta description for a textpage"));
}
[Test]
@@ -631,7 +632,7 @@ namespace Umbraco.Tests.Models
// Assert
Assert.That(content.Properties.Contains("author"), Is.True);
Assert.That(content.Properties.Contains("keywords"), Is.False);
- Assert.That(content.Properties.Contains("metaDescription"), Is.False);
+ Assert.That(content.Properties.Contains("description"), Is.False);
}
[Test]
@@ -828,7 +829,7 @@ namespace Umbraco.Tests.Models
public void Can_Avoid_Circular_Dependencies_In_Composition()
{
var textPage = MockedContentTypes.CreateTextpageContentType();
- var parent = MockedContentTypes.CreateSimpleContentType("parent", "Parent");
+ var parent = MockedContentTypes.CreateSimpleContentType("parent", "Parent", null, true);
var meta = MockedContentTypes.CreateMetaContentType();
var mixin1 = MockedContentTypes.CreateSimpleContentType("mixin1", "Mixin1", new PropertyTypeCollection(
new List
@@ -863,7 +864,9 @@ namespace Umbraco.Tests.Models
var addedMetaMixin2 = mixin2.AddContentType(meta);
var addedMixin2 = mixin1.AddContentType(mixin2);
var addedMeta = parent.AddContentType(meta);
+
var addedMixin1 = parent.AddContentType(mixin1);
+
var addedMixin1Textpage = textPage.AddContentType(mixin1);
var addedTextpageParent = parent.AddContentType(textPage);
diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs
index 11b925e9fd..ac7bae8ce2 100644
--- a/src/Umbraco.Tests/Models/ContentXmlTest.cs
+++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs
@@ -69,7 +69,7 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(content.Properties["title"].Value.ToString(), element.Elements("title").Single().Value);
Assert.AreEqual(content.Properties["bodyText"].Value.ToString(), element.Elements("bodyText").Single().Value);
Assert.AreEqual(content.Properties["keywords"].Value.ToString(), element.Elements("keywords").Single().Value);
- Assert.AreEqual(content.Properties["metaDescription"].Value.ToString(), element.Elements("metaDescription").Single().Value);
+ Assert.AreEqual(content.Properties["description"].Value.ToString(), element.Elements("description").Single().Value);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs
index c9ed53ef2a..f5ffee1ce3 100644
--- a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs
+++ b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs
@@ -408,7 +408,7 @@ namespace Umbraco.Tests.Mvc
var ctx = new UmbracoContext(
GetHttpContextFactory(url, routeData).HttpContext,
appCtx,
- new PublishedCaches(cache, new PublishedMediaCache()),
+ new PublishedCaches(cache, new PublishedMediaCache(appCtx)),
new WebSecurity(http, appCtx));
//if (setSingleton)
diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs
index f9bc86de62..5d94e604a4 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs
@@ -193,8 +193,8 @@ namespace Umbraco.Tests.Persistence.Repositories
using (var repository = CreateRepository(unitOfWork))
{
var ctMain = MockedContentTypes.CreateSimpleContentType();
- var ctChild1 = MockedContentTypes.CreateSimpleContentType("child1", "Child 1", ctMain);
- var ctChild2 = MockedContentTypes.CreateSimpleContentType("child2", "Child 2", ctChild1);
+ var ctChild1 = MockedContentTypes.CreateSimpleContentType("child1", "Child 1", ctMain, true);
+ var ctChild2 = MockedContentTypes.CreateSimpleContentType("child2", "Child 2", ctChild1, true);
repository.AddOrUpdate(ctMain);
repository.AddOrUpdate(ctChild1);
@@ -282,14 +282,14 @@ namespace Umbraco.Tests.Persistence.Repositories
var contentType = repository.Get(NodeDto.NodeIdSeed + 1);
// Act
- contentType.PropertyGroups["Meta"].PropertyTypes.Remove("metaDescription");
+ contentType.PropertyGroups["Meta"].PropertyTypes.Remove("description");
repository.AddOrUpdate(contentType);
unitOfWork.Commit();
var result = repository.Get(NodeDto.NodeIdSeed + 1);
// Assert
- Assert.That(result.PropertyTypes.Any(x => x.Alias == "metaDescription"), Is.False);
+ Assert.That(result.PropertyTypes.Any(x => x.Alias == "description"), Is.False);
Assert.That(contentType.PropertyGroups.Count, Is.EqualTo(result.PropertyGroups.Count));
Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(result.PropertyTypes.Count()));
}
@@ -434,7 +434,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// 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"));
+ Assert.That(subpage.Properties.First(x => x.Alias == "description").Value, Is.EqualTo("This is the meta description for a textpage"));
}
}
@@ -534,7 +534,7 @@ namespace Umbraco.Tests.Persistence.Repositories
//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(updated.Properties.First(x => x.Alias == "description").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);
diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs
index bd35197193..c3524580dd 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs
@@ -658,14 +658,20 @@ namespace Umbraco.Tests.Persistence.Repositories
new[]
{
new Tag {Text = "tag1", Group = "test"},
- new Tag {Text = "tag2", Group = "test1"}
+ new Tag {Text = "tag4", Group = "test1"}
}, false);
var result1 = repository.GetTagsForEntityType(TaggableObjectTypes.Content).ToArray();
var result2 = repository.GetTagsForEntityType(TaggableObjectTypes.Media).ToArray();
+ var result3 = repository.GetTagsForEntityType(TaggableObjectTypes.All).ToArray();
Assert.AreEqual(3, result1.Count());
Assert.AreEqual(2, result2.Count());
+ Assert.AreEqual(4, result3.Count());
+
+ Assert.AreEqual(1, result1.Single(x => x.Text == "tag1").NodeCount);
+ Assert.AreEqual(2, result3.Single(x => x.Text == "tag1").NodeCount);
+ Assert.AreEqual(1, result3.Single(x => x.Text == "tag4").NodeCount);
}
}
diff --git a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs
index 037d728b90..a8fae01850 100644
--- a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs
+++ b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs
@@ -277,7 +277,7 @@ namespace Umbraco.Tests.Plugins
public void Resolves_Attributed_Trees()
{
var trees = PluginManager.Current.ResolveAttributedTrees();
- Assert.AreEqual(18, trees.Count());
+ Assert.AreEqual(19, trees.Count());
}
[Test]
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
index b94dc9892d..03e13acdd5 100644
--- a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
+++ b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
@@ -92,9 +92,9 @@ namespace Umbraco.Tests.PublishedCache
_umbracoContext = new UmbracoContext(
_httpContextFactory.HttpContext,
- ApplicationContext.Current,
- new PublishedCaches(cache, new PublishedMediaCache()),
- new WebSecurity(_httpContextFactory.HttpContext, ApplicationContext.Current));
+ ApplicationContext,
+ new PublishedCaches(cache, new PublishedMediaCache(ApplicationContext)),
+ new WebSecurity(_httpContextFactory.HttpContext, ApplicationContext));
_cache = _umbracoContext.ContentCache;
}
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs
index 53a8b5b658..6d0b5b18b7 100644
--- a/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs
+++ b/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs
@@ -37,7 +37,7 @@ namespace Umbraco.Tests.PublishedCache
var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot2.Id);
var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application), ctx);
var roots = cache.GetAtRoot();
Assert.AreEqual(2, roots.Count());
Assert.IsTrue(roots.Select(x => x.Id).ContainsAll(new[] {mRoot1.Id, mRoot2.Id}));
@@ -126,6 +126,8 @@ namespace Umbraco.Tests.PublishedCache
[Test]
public void Convert_From_Search_Result()
{
+ var ctx = GetUmbracoContext("/test", 1234);
+
var result = new SearchResult()
{
Id = 1234,
@@ -144,7 +146,7 @@ namespace Umbraco.Tests.PublishedCache
result.Fields.Add("updateDate", "2012-07-16T10:34:09");
result.Fields.Add("writerName", "Shannon");
- var store = new PublishedMediaCache();
+ var store = new PublishedMediaCache(ctx.Application);
var doc = store.ConvertFromSearchResult(result);
DoAssert(doc, 1234, 0, 0, "", "Image", 0, "Shannon", "", 0, 0, "-1,1234", default(DateTime), DateTime.Parse("2012-07-16T10:34:09"), 2);
@@ -154,9 +156,11 @@ namespace Umbraco.Tests.PublishedCache
[Test]
public void Convert_From_XPath_Navigator()
{
+ var ctx = GetUmbracoContext("/test", 1234);
+
var xmlDoc = GetMediaXml();
var navigator = xmlDoc.SelectSingleNode("/root/Image").CreateNavigator();
- var cache = new PublishedMediaCache();
+ var cache = new PublishedMediaCache(ctx.Application);
var doc = cache.ConvertFromXPathNavigator(navigator);
DoAssert(doc, 2000, 0, 2, "image1", "Image", 2044, "Shannon", "Shannon2", 22, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1);
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs
index 5a9067a095..e2e1b91087 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs
@@ -74,7 +74,8 @@ namespace Umbraco.Tests.PublishedContent
{
var contentTypeService = ctx.Application.Services.ContentTypeService;
var baseType = new ContentType(-1) {Alias = "base", Name = "Base"};
- var inheritedType = new ContentType(baseType) {Alias = "inherited", Name = "Inherited"};
+ const string contentTypeAlias = "inherited";
+ var inheritedType = new ContentType(baseType, contentTypeAlias) {Alias = contentTypeAlias, Name = "Inherited"};
contentTypeService.Save(baseType);
contentTypeService.Save(inheritedType);
createContentTypes = false;
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
index d776e61776..478335af15 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
@@ -49,7 +49,7 @@ namespace Umbraco.Tests.PublishedContent
});
PublishedCachesResolver.Current = new PublishedCachesResolver(new PublishedCaches(
- new PublishedContentCache(), new PublishedMediaCache()));
+ new PublishedContentCache(), new PublishedMediaCache(ApplicationContext)));
if (PublishedContentModelFactoryResolver.HasCurrent == false)
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs
index 6d2b85ff3a..425da6ac2e 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs
@@ -11,6 +11,7 @@ using Examine.LuceneEngine;
using Examine.LuceneEngine.Providers;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Store;
+using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration;
@@ -60,7 +61,7 @@ namespace Umbraco.Tests.PublishedContent
internal static IPublishedContent GetNode(int id, UmbracoContext umbracoContext)
{
var ctx = umbracoContext;
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application), ctx);
var doc = cache.GetById(id);
Assert.IsNotNull(doc);
return doc;
@@ -112,7 +113,7 @@ namespace Umbraco.Tests.PublishedContent
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
var publishedMedia = cache.GetById(1111);
@@ -141,7 +142,7 @@ namespace Umbraco.Tests.PublishedContent
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//ensure it is found
var publishedMedia = cache.GetById(3113);
@@ -181,7 +182,7 @@ namespace Umbraco.Tests.PublishedContent
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
var publishedMedia = cache.GetById(1111);
@@ -203,7 +204,7 @@ namespace Umbraco.Tests.PublishedContent
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
var publishedMedia = cache.GetById(1111);
@@ -225,7 +226,7 @@ namespace Umbraco.Tests.PublishedContent
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
var publishedMedia = cache.GetById(1111);
@@ -247,7 +248,7 @@ namespace Umbraco.Tests.PublishedContent
indexer.RebuildIndex();
var ctx = GetUmbracoContext("/test", 1234);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
var publishedMedia = cache.GetById(3113);
@@ -266,7 +267,7 @@ namespace Umbraco.Tests.PublishedContent
indexer.RebuildIndex();
var ctx = GetUmbracoContext("/test", 1234);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
+ var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
var publishedMedia = cache.GetById(3113);
@@ -412,6 +413,100 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsTrue(publishedSubChild1.AncestorsOrSelf().Select(x => x.Id).ContainsAll(
new[] { mSubChild1.Id, mChild1.Id, mRoot.Id }));
}
+
+ [Test]
+ public void Convert_From_Legacy_Xml()
+ {
+ var config = SettingsForTests.GenerateMockSettings();
+
+ var contentMock = Mock.Get(config.Content);
+ contentMock.Setup(x => x.UseLegacyXmlSchema).Returns(true);
+
+ SettingsForTests.ConfigureSettings(config);
+
+ var nodeId = 2112;
+
+ var xml = XElement.Parse(@"
+
+ 115
+ 268
+ 10726
+ jpg
+
+
+ 115
+ 268
+ 10726
+ jpg
+
+ ");
+ var node = xml.DescendantsAndSelf("node").Single(x => (int) x.Attribute("id") == nodeId);
+
+ var publishedMedia = new PublishedMediaCache(ApplicationContext);
+
+ var nav = node.CreateNavigator();
+
+ var converted = publishedMedia.ConvertFromXPathNodeIterator(nav.Select("/node"), nodeId);
+
+ Assert.AreEqual(nodeId, converted.Id);
+ Assert.AreEqual(3, converted.Level);
+ Assert.AreEqual(1, converted.SortOrder);
+ Assert.AreEqual("Sam's Umbraco Image", converted.Name);
+ Assert.AreEqual("-1,1111,2222,2112", converted.Path);
+ }
+
+ [Test]
+ public void Convert_From_Standard_Xml()
+ {
+ var config = SettingsForTests.GenerateMockSettings();
+
+ var contentMock = Mock.Get(config.Content);
+ contentMock.Setup(x => x.UseLegacyXmlSchema).Returns(true);
+
+ SettingsForTests.ConfigureSettings(config);
+
+ var nodeId = 2112;
+
+ var xml = XElement.Parse(@"
+
+ 115
+ 268
+ 10726
+ jpg
+
+
+ 115
+ 268
+ 10726
+ jpg
+
+ ");
+ var node = xml.DescendantsAndSelf("Image").Single(x => (int)x.Attribute("id") == nodeId);
+
+ var publishedMedia = new PublishedMediaCache(ApplicationContext);
+
+ var nav = node.CreateNavigator();
+
+ var converted = publishedMedia.ConvertFromXPathNodeIterator(nav.Select("/Image"), nodeId);
+
+ Assert.AreEqual(nodeId, converted.Id);
+ Assert.AreEqual(3, converted.Level);
+ Assert.AreEqual(1, converted.SortOrder);
+ Assert.AreEqual("Sam's Umbraco Image", converted.Name);
+ Assert.AreEqual("-1,1111,2222,2112", converted.Path);
+ }
+
+ [Test]
+ public void Detects_Error_In_Xml()
+ {
+ var errorXml = new XElement("error", string.Format("No media is maching '{0}'", 1234));
+ var nav = errorXml.CreateNavigator();
+
+ var publishedMedia = new PublishedMediaCache(ApplicationContext);
+ var converted = publishedMedia.ConvertFromXPathNodeIterator(nav.Select("/"), 1234);
+
+ Assert.IsNull(converted);
+ }
}
diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs
index 8162b27c62..8b1981bc9e 100644
--- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs
+++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs
@@ -230,7 +230,7 @@ namespace Umbraco.Tests.Scheduling
//wait till the thread is done
await tManager;
-
+
foreach (var task in tasks)
{
Assert.IsTrue(task.Ended != default(DateTime));
@@ -241,6 +241,7 @@ namespace Umbraco.Tests.Scheduling
Assert.IsFalse(tManager.IsDisposed);
}
}
+
private class MyTask : BaseTask
{
diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs
index 7766f8a929..5714ec2c9f 100644
--- a/src/Umbraco.Tests/Services/ContentServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs
@@ -1335,7 +1335,7 @@ namespace Umbraco.Tests.Services
//MCH: I'm guessing this is an issue because of the format the date is actually stored as, right? Cause we don't do any formatting when saving or loading
Assert.That(sut.GetValue("dateTime").ToString("G"), Is.EqualTo(content.GetValue("dateTime").ToString("G")));
Assert.That(sut.GetValue("colorPicker"), Is.EqualTo("black"));
- Assert.That(sut.GetValue("folderBrowser"), Is.Empty);
+ Assert.That(sut.GetValue("folderBrowser"), Is.Null);
Assert.That(sut.GetValue("ddlMultiple"), Is.EqualTo("1234,1235"));
Assert.That(sut.GetValue("rbList"), Is.EqualTo("random"));
Assert.That(sut.GetValue("date").ToString("G"), Is.EqualTo(content.GetValue("date").ToString("G")));
diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
index 892076f54f..f430a93e23 100644
--- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
@@ -1,3 +1,4 @@
+using System.Runtime.Remoting;
using NUnit.Framework;
using System;
using System.Collections.Generic;
@@ -5,6 +6,8 @@ using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.Caching;
+using Umbraco.Tests.CodeFirst.TestModels.Composition;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
@@ -178,10 +181,11 @@ namespace Umbraco.Tests.Services
/*,"Navigation"*/);
cts.Save(ctBase);
- var ctHomePage = new ContentType(ctBase)
+ const string contentTypeAlias = "HomePage";
+ var ctHomePage = new ContentType(ctBase, contentTypeAlias)
{
Name = "Home Page",
- Alias = "HomePage",
+ Alias = contentTypeAlias,
Icon = "settingDomain.gif",
Thumbnail = "folder.png",
AllowedAsRoot = true
@@ -191,7 +195,7 @@ namespace Umbraco.Tests.Services
cts.Save(ctHomePage);
// Act
- var homeDoc = cs.CreateContent("Home Page", -1, "HomePage");
+ var homeDoc = cs.CreateContent("Home Page", -1, contentTypeAlias);
cs.SaveAndPublishWithStatus(homeDoc);
// Assert
@@ -243,13 +247,13 @@ namespace Umbraco.Tests.Services
var global = MockedContentTypes.CreateSimpleContentType("global", "Global");
service.Save(global);
- var components = MockedContentTypes.CreateSimpleContentType("components", "Components", global);
+ var components = MockedContentTypes.CreateSimpleContentType("components", "Components", global, true);
service.Save(components);
- var component = MockedContentTypes.CreateSimpleContentType("component", "Component", components);
+ var component = MockedContentTypes.CreateSimpleContentType("component", "Component", components, true);
service.Save(component);
- var category = MockedContentTypes.CreateSimpleContentType("category", "Category", global);
+ var category = MockedContentTypes.CreateSimpleContentType("category", "Category", global, true);
service.Save(category);
var success = category.AddContentType(component);
@@ -339,10 +343,10 @@ namespace Umbraco.Tests.Services
var parentContentType1 = MockedContentTypes.CreateSimpleContentType("parent1", "Parent1");
service.Save(parentContentType1);
- var parentContentType2 = MockedContentTypes.CreateSimpleContentType("parent2", "Parent2");
+ var parentContentType2 = MockedContentTypes.CreateSimpleContentType("parent2", "Parent2", null, true);
service.Save(parentContentType2);
- var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1);
+ var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1, true);
service.Save(simpleContentType);
// Act
@@ -371,8 +375,8 @@ namespace Umbraco.Tests.Services
Assert.AreNotEqual(clonedContentType.Key, originalContentType.Key);
Assert.AreNotEqual(clonedContentType.Path, originalContentType.Path);
- Assert.AreNotEqual(clonedContentType.PropertyTypes.First(x => x.Alias.Equals("title")).Id, originalContentType.PropertyTypes.First(x => x.Alias.Equals("title")).Id);
- Assert.AreNotEqual(clonedContentType.PropertyGroups.First(x => x.Name.Equals("Content")).Id, originalContentType.PropertyGroups.First(x => x.Name.Equals("Content")).Id);
+ Assert.AreNotEqual(clonedContentType.PropertyTypes.First(x => x.Alias.StartsWith("title")).Id, originalContentType.PropertyTypes.First(x => x.Alias.StartsWith("title")).Id);
+ Assert.AreNotEqual(clonedContentType.PropertyGroups.First(x => x.Name.StartsWith("Content")).Id, originalContentType.PropertyGroups.First(x => x.Name.StartsWith("Content")).Id);
}
[Test]
@@ -432,10 +436,10 @@ namespace Umbraco.Tests.Services
var parentContentType1 = MockedContentTypes.CreateSimpleContentType("parent1", "Parent1");
service.Save(parentContentType1);
- var parentContentType2 = MockedContentTypes.CreateSimpleContentType("parent2", "Parent2");
+ var parentContentType2 = MockedContentTypes.CreateSimpleContentType("parent2", "Parent2", null, true);
service.Save(parentContentType2);
- var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1);
+ var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1, true);
service.Save(simpleContentType);
// Act
@@ -460,8 +464,768 @@ namespace Umbraco.Tests.Services
Assert.AreNotEqual(clonedContentType.Key, originalContentType.Key);
Assert.AreNotEqual(clonedContentType.Path, originalContentType.Path);
- Assert.AreNotEqual(clonedContentType.PropertyTypes.First(x => x.Alias.Equals("title")).Id, originalContentType.PropertyTypes.First(x => x.Alias.Equals("title")).Id);
- Assert.AreNotEqual(clonedContentType.PropertyGroups.First(x => x.Name.Equals("Content")).Id, originalContentType.PropertyGroups.First(x => x.Name.Equals("Content")).Id);
+ Assert.AreNotEqual(clonedContentType.PropertyTypes.First(x => x.Alias.StartsWith("title")).Id, originalContentType.PropertyTypes.First(x => x.Alias.StartsWith("title")).Id);
+ Assert.AreNotEqual(clonedContentType.PropertyGroups.First(x => x.Name.StartsWith("Content")).Id, originalContentType.PropertyGroups.First(x => x.Name.StartsWith("Content")).Id);
+ }
+
+ [Test]
+ public void Cannot_Add_Duplicate_PropertyType_Alias_To_Referenced_Composition()
+ {
+ //Related the second issue in screencast from this post http://issues.umbraco.org/issue/U4-5986
+
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+
+ var parent = MockedContentTypes.CreateSimpleContentType();
+ service.Save(parent);
+ var child = MockedContentTypes.CreateSimpleContentType("simpleChildPage", "Simple Child Page", parent, true);
+ service.Save(child);
+ var composition = MockedContentTypes.CreateMetaContentType();
+ service.Save(composition);
+
+ //Adding Meta-composition to child doc type
+ child.AddContentType(composition);
+ service.Save(child);
+
+ // Act
+ var duplicatePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var added = composition.AddPropertyType(duplicatePropertyType, "Meta");
+
+ // Assert
+ Assert.That(added, Is.True);
+ Assert.Throws(() => service.Save(composition));
+ Assert.DoesNotThrow(() => service.GetContentType("simpleChildPage"));
+ }
+
+ [Test]
+ public void Cannot_Add_Duplicate_PropertyType_Alias_In_Composition_Graph()
+ {
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+
+ var basePage = MockedContentTypes.CreateSimpleContentType("basePage", "Base Page", null, true);
+ service.Save(basePage);
+ var contentPage = MockedContentTypes.CreateSimpleContentType("contentPage", "Content Page", basePage);
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateSimpleContentType("advancedPage", "Advanced Page", contentPage, true);
+ service.Save(advancedPage);
+
+ var metaComposition = MockedContentTypes.CreateMetaContentType();
+ service.Save(metaComposition);
+ var seoComposition = MockedContentTypes.CreateSeoContentType();
+ service.Save(seoComposition);
+
+ var metaAdded = contentPage.AddContentType(metaComposition);
+ service.Save(contentPage);
+ var seoAdded = advancedPage.AddContentType(seoComposition);
+ service.Save(advancedPage);
+
+ // Act
+ var duplicatePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var addedToBasePage = basePage.AddPropertyType(duplicatePropertyType, "Content");
+ var addedToAdvancedPage = advancedPage.AddPropertyType(duplicatePropertyType, "Content");
+ var addedToMeta = metaComposition.AddPropertyType(duplicatePropertyType, "Meta");
+ var addedToSeo = seoComposition.AddPropertyType(duplicatePropertyType, "Seo");
+
+ // Assert
+ Assert.That(metaAdded, Is.True);
+ Assert.That(seoAdded, Is.True);
+
+ Assert.That(addedToBasePage, Is.True);
+ Assert.That(addedToAdvancedPage, Is.False);
+ Assert.That(addedToMeta, Is.True);
+ Assert.That(addedToSeo, Is.True);
+
+ Assert.Throws(() => service.Save(basePage));
+ Assert.Throws(() => service.Save(metaComposition));
+ Assert.Throws(() => service.Save(seoComposition));
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("meta"));
+ Assert.DoesNotThrow(() => service.GetContentType("seo"));
+ }
+
+ [Test]
+ public void Cannot_Add_Duplicate_PropertyType_Alias_At_Root_Which_Conflicts_With_Third_Levels_Composition()
+ {
+ /*
+ * BasePage, gets 'Title' added but should not be allowed
+ * -- Content Page
+ * ---- Advanced Page -> Content Meta
+ * Content Meta :: Composition, has 'Title'
+ *
+ * Content Meta has 'Title' PropertyType
+ * Adding 'Title' to BasePage should fail
+ */
+
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var basePage = MockedContentTypes.CreateBasicContentType();
+ service.Save(basePage);
+ var contentPage = MockedContentTypes.CreateBasicContentType("contentPage", "Content Page", basePage);
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateBasicContentType("advancedPage", "Advanced Page", contentPage);
+ service.Save(advancedPage);
+
+ var contentMetaComposition = MockedContentTypes.CreateContentMetaContentType();
+ service.Save(contentMetaComposition);
+
+ // Act
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
+ service.Save(basePage);
+
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
+ service.Save(contentPage);
+
+ var compositionAdded = advancedPage.AddContentType(contentMetaComposition);
+ service.Save(advancedPage);
+
+ //NOTE: It should not be possible to Save 'BasePage' with the Title PropertyType added
+ var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var titleAdded = basePage.AddPropertyType(titlePropertyType, "Content");
+
+ // Assert
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+ Assert.That(titleAdded, Is.True);
+ Assert.That(compositionAdded, Is.True);
+
+ Assert.Throws(() => service.Save(basePage));
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+ }
+
+ [Test]
+ public void Cannot_Rename_PropertyType_Alias_On_Composition_Which_Would_Cause_Conflict_In_Other_Composition()
+ {
+ /*
+ * Meta renames alias to 'title'
+ * Seo has 'Title'
+ * BasePage
+ * -- ContentPage
+ * ---- AdvancedPage -> Seo
+ * ------ MoreAdvanedPage -> Meta
+ */
+
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var basePage = MockedContentTypes.CreateBasicContentType();
+ service.Save(basePage);
+ var contentPage = MockedContentTypes.CreateBasicContentType("contentPage", "Content Page", basePage);
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateBasicContentType("advancedPage", "Advanced Page", contentPage);
+ service.Save(advancedPage);
+ var moreAdvancedPage = MockedContentTypes.CreateBasicContentType("moreAdvancedPage", "More Advanced Page", advancedPage);
+ service.Save(moreAdvancedPage);
+
+ var seoComposition = MockedContentTypes.CreateSeoContentType();
+ service.Save(seoComposition);
+ var metaComposition = MockedContentTypes.CreateMetaContentType();
+ service.Save(metaComposition);
+
+ // Act
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
+ service.Save(basePage);
+
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
+ service.Save(contentPage);
+
+ var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var subtitleAdded = advancedPage.AddPropertyType(subtitlePropertyType, "Content");
+ service.Save(advancedPage);
+
+ var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var titleAdded = seoComposition.AddPropertyType(titlePropertyType, "Content");
+ service.Save(seoComposition);
+
+ var seoCompositionAdded = advancedPage.AddContentType(seoComposition);
+ var metaCompositionAdded = moreAdvancedPage.AddContentType(metaComposition);
+ service.Save(advancedPage);
+ service.Save(moreAdvancedPage);
+
+ var keywordsPropertyType = metaComposition.PropertyTypes.First(x => x.Alias.Equals("metakeywords"));
+ keywordsPropertyType.Alias = "title";
+
+ // Assert
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(subtitleAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+ Assert.That(titleAdded, Is.True);
+ Assert.That(seoCompositionAdded, Is.True);
+ Assert.That(metaCompositionAdded, Is.True);
+
+ Assert.Throws(() => service.Save(metaComposition));
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("moreAdvancedPage"));
+ }
+
+ [Test]
+ public void Can_Add_Additional_Properties_On_Composition_Once_Composition_Has_Been_Saved()
+ {
+ /*
+ * Meta renames alias to 'title'
+ * Seo has 'Title'
+ * BasePage
+ * -- ContentPage
+ * ---- AdvancedPage -> Seo
+ * ------ MoreAdvancedPage -> Meta
+ */
+
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var basePage = MockedContentTypes.CreateBasicContentType();
+ service.Save(basePage);
+ var contentPage = MockedContentTypes.CreateBasicContentType("contentPage", "Content Page", basePage);
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateBasicContentType("advancedPage", "Advanced Page", contentPage);
+ service.Save(advancedPage);
+ var moreAdvancedPage = MockedContentTypes.CreateBasicContentType("moreAdvancedPage", "More Advanced Page", advancedPage);
+ service.Save(moreAdvancedPage);
+
+ var seoComposition = MockedContentTypes.CreateSeoContentType();
+ service.Save(seoComposition);
+ var metaComposition = MockedContentTypes.CreateMetaContentType();
+ service.Save(metaComposition);
+
+ // Act
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
+ service.Save(basePage);
+
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
+ service.Save(contentPage);
+
+ var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var subtitleAdded = advancedPage.AddPropertyType(subtitlePropertyType, "Content");
+ service.Save(advancedPage);
+
+ var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var titleAdded = seoComposition.AddPropertyType(titlePropertyType, "Content");
+ service.Save(seoComposition);
+
+ var seoCompositionAdded = advancedPage.AddContentType(seoComposition);
+ var metaCompositionAdded = moreAdvancedPage.AddContentType(metaComposition);
+ service.Save(advancedPage);
+ service.Save(moreAdvancedPage);
+
+ // Assert
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(subtitleAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+ Assert.That(titleAdded, Is.True);
+ Assert.That(seoCompositionAdded, Is.True);
+ Assert.That(metaCompositionAdded, Is.True);
+
+ var testPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "test", Name = "Test", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var testAdded = seoComposition.AddPropertyType(testPropertyType, "Content");
+ service.Save(seoComposition);
+
+ Assert.That(testAdded, Is.True);
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("moreAdvancedPage"));
+ }
+
+ [Test]
+ public void Cannot_Rename_PropertyGroup_On_Child_Avoiding_Conflict_With_Parent_PropertyGroup()
+ {
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var page = MockedContentTypes.CreateSimpleContentType("page", "Page", null, true, "Content");
+ service.Save(page);
+ var contentPage = MockedContentTypes.CreateSimpleContentType("contentPage", "Content Page", page, true, "Content_");
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateSimpleContentType("advancedPage", "Advanced Page", contentPage, true, "Details");
+ service.Save(advancedPage);
+
+ var contentMetaComposition = MockedContentTypes.CreateContentMetaContentType();
+ service.Save(contentMetaComposition);
+
+ // Act
+ var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");
+ var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
+ service.Save(contentPage);
+
+ var compositionAdded = contentPage.AddContentType(contentMetaComposition);
+ service.Save(contentPage);
+
+ //Change the name of the tab on the "root" content type 'page'.
+ var propertyGroup = contentPage.PropertyGroups["Content_"];
+ Assert.Throws(() => contentPage.PropertyGroups.Add(new PropertyGroup
+ {
+ Id = propertyGroup.Id,
+ Name = "Content",
+ SortOrder = 0
+ }));
+
+ // Assert
+ Assert.That(compositionAdded, Is.True);
+ Assert.That(subtitleAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+ }
+
+ [Test]
+ public void Cannot_Rename_PropertyType_Alias_Causing_Conflicts_With_Parents()
+ {
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var basePage = MockedContentTypes.CreateBasicContentType();
+ service.Save(basePage);
+ var contentPage = MockedContentTypes.CreateBasicContentType("contentPage", "Content Page", basePage);
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateBasicContentType("advancedPage", "Advanced Page", contentPage);
+ service.Save(advancedPage);
+
+ // Act
+ var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var titleAdded = basePage.AddPropertyType(titlePropertyType, "Content");
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = contentPage.AddPropertyType(bodyTextPropertyType, "Content");
+ var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorAdded = advancedPage.AddPropertyType(authorPropertyType, "Content");
+ service.Save(basePage);
+ service.Save(contentPage);
+ service.Save(advancedPage);
+
+ //Rename the PropertyType to something that already exists in the Composition - NOTE this should not be allowed and Saving should throw an exception
+ var authorPropertyTypeToRename = advancedPage.PropertyTypes.First(x => x.Alias.Equals("author"));
+ authorPropertyTypeToRename.Alias = "title";
+
+ // Assert
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+ Assert.That(titleAdded, Is.True);
+ Assert.That(subtitleAdded, Is.True);
+
+ Assert.Throws(() => service.Save(advancedPage));
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+ }
+
+ [Test]
+ public void Can_Add_PropertyType_Alias_Which_Exists_In_Composition_Outside_Graph()
+ {
+ /*
+ * Meta (Composition)
+ * Content Meta (Composition) has 'Title' -> Meta
+ * BasePage
+ * -- ContentPage gets 'Title' added -> Meta
+ * ---- Advanced Page
+ */
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+
+ var basePage = MockedContentTypes.CreateSimpleContentType("basePage", "Base Page", null, true);
+ service.Save(basePage);
+ var contentPage = MockedContentTypes.CreateSimpleContentType("contentPage", "Content Page", basePage, true);
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateSimpleContentType("advancedPage", "Advanced Page", contentPage, true);
+ service.Save(advancedPage);
+
+ var metaComposition = MockedContentTypes.CreateMetaContentType();
+ service.Save(metaComposition);
+
+ var contentMetaComposition = MockedContentTypes.CreateContentMetaContentType();
+ service.Save(contentMetaComposition);
+
+ var metaAdded = contentPage.AddContentType(metaComposition);
+ service.Save(contentPage);
+
+ var metaAddedToComposition = contentMetaComposition.AddContentType(metaComposition);
+ service.Save(contentMetaComposition);
+
+ // Act
+ var propertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var addedToContentPage = contentPage.AddPropertyType(propertyType, "Content");
+
+ // Assert
+ Assert.That(metaAdded, Is.True);
+ Assert.That(metaAddedToComposition, Is.True);
+
+ Assert.That(addedToContentPage, Is.True);
+ Assert.DoesNotThrow(() => service.Save(contentPage));
+ }
+
+ [Test]
+ public void Can_Rename_PropertyGroup_With_Inherited_PropertyGroups()
+ {
+ //Related the first issue in screencast from this post http://issues.umbraco.org/issue/U4-5986
+
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+
+ var page = MockedContentTypes.CreateSimpleContentType("page", "Page", null, false, "Content_");
+ service.Save(page);
+ var contentPage = MockedContentTypes.CreateSimpleContentType("contentPage", "Content Page", page, true);
+ service.Save(contentPage);
+ var composition = MockedContentTypes.CreateMetaContentType();
+ composition.AddPropertyGroup("Content");
+ service.Save(composition);
+ //Adding Meta-composition to child doc type
+ contentPage.AddContentType(composition);
+ service.Save(contentPage);
+
+ // Act
+ var propertyTypeOne = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "testTextbox", Name = "Test Textbox", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var firstOneAdded = contentPage.AddPropertyType(propertyTypeOne, "Content_");
+ var propertyTypeTwo = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "anotherTextbox", Name = "Another Test Textbox", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var secondOneAdded = contentPage.AddPropertyType(propertyTypeTwo, "Content");
+ service.Save(contentPage);
+
+ Assert.That(page.PropertyGroups.Contains("Content_"), Is.True);
+ var propertyGroup = page.PropertyGroups["Content_"];
+ page.PropertyGroups.Add(new PropertyGroup{ Id = propertyGroup.Id, Name = "ContentTab", SortOrder = 0});
+ service.Save(page);
+
+ // Assert
+ Assert.That(firstOneAdded, Is.True);
+ Assert.That(secondOneAdded, Is.True);
+
+ var contentType = service.GetContentType("contentPage");
+ Assert.That(contentType, Is.Not.Null);
+
+ var compositionPropertyGroups = contentType.CompositionPropertyGroups;
+ Assert.That(compositionPropertyGroups.Count(x => x.Name.Equals("Content_")), Is.EqualTo(0));
+
+ var propertyTypeCount = contentType.PropertyTypes.Count();
+ var compPropertyTypeCount = contentType.CompositionPropertyTypes.Count();
+ Assert.That(propertyTypeCount, Is.EqualTo(5));
+ Assert.That(compPropertyTypeCount, Is.EqualTo(10));
+ }
+
+ [Test]
+ public void Can_Rename_PropertyGroup_On_Parent_Without_Causing_Duplicate_PropertyGroups()
+ {
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var page = MockedContentTypes.CreateSimpleContentType("page", "Page", null, true, "Content_");
+ service.Save(page);
+ var contentPage = MockedContentTypes.CreateSimpleContentType("contentPage", "Content Page", page, true, "Contentx");
+ service.Save(contentPage);
+ var advancedPage = MockedContentTypes.CreateSimpleContentType("advancedPage", "Advanced Page", contentPage, true, "Contenty");
+ service.Save(advancedPage);
+
+ var contentMetaComposition = MockedContentTypes.CreateContentMetaContentType();
+ service.Save(contentMetaComposition);
+ var compositionAdded = contentPage.AddContentType(contentMetaComposition);
+ service.Save(contentPage);
+
+ // Act
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = contentPage.AddPropertyType(bodyTextPropertyType, "Content_");//Will be added to the parent tab
+ var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");//Will be added to the "Content Meta" composition
+ service.Save(contentPage);
+
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var descriptionPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "description", Name = "Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var keywordsPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "keywords", Name = "Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorAdded = advancedPage.AddPropertyType(authorPropertyType, "Content_");//Will be added to an ancestor tab
+ var descriptionAdded = advancedPage.AddPropertyType(descriptionPropertyType, "Contentx");//Will be added to a parent tab
+ var keywordsAdded = advancedPage.AddPropertyType(keywordsPropertyType, "Content");//Will be added to the "Content Meta" composition
+ service.Save(advancedPage);
+
+ //Change the name of the tab on the "root" content type 'page'.
+ var propertyGroup = page.PropertyGroups["Content_"];
+ page.PropertyGroups.Add(new PropertyGroup { Id = propertyGroup.Id, Name = "Content", SortOrder = 0 });
+ service.Save(page);
+
+ // Assert
+ Assert.That(compositionAdded, Is.True);
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(subtitleAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+ Assert.That(descriptionAdded, Is.True);
+ Assert.That(keywordsAdded, Is.True);
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+
+ var advancedPageReloaded = service.GetContentType("advancedPage");
+ var contentUnderscoreTabExists = advancedPageReloaded.CompositionPropertyGroups.Any(x => x.Name.Equals("Content_"));
+ Assert.That(contentUnderscoreTabExists, Is.False);
+
+ var numberOfContentTabs = advancedPageReloaded.CompositionPropertyGroups.Count(x => x.Name.Equals("Content"));
+ Assert.That(numberOfContentTabs, Is.EqualTo(4));
+ }
+
+ [Test]
+ public void Can_Rename_PropertyGroup_On_Parent_Without_Causing_Duplicate_PropertyGroups_v2()
+ {
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var page = MockedContentTypes.CreateSimpleContentType("page", "Page", null, true, "Content_");
+ service.Save(page);
+ var contentPage = MockedContentTypes.CreateSimpleContentType("contentPage", "Content Page", page, true, "Content");
+ service.Save(contentPage);
+
+ var contentMetaComposition = MockedContentTypes.CreateContentMetaContentType();
+ service.Save(contentMetaComposition);
+
+ // Act
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = page.AddPropertyType(bodyTextPropertyType, "Content_");
+ var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");
+ var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content_");
+ service.Save(page);
+ service.Save(contentPage);
+
+ var compositionAdded = contentPage.AddContentType(contentMetaComposition);
+ service.Save(contentPage);
+
+ //Change the name of the tab on the "root" content type 'page'.
+ var propertyGroup = page.PropertyGroups["Content_"];
+ page.PropertyGroups.Add(new PropertyGroup { Id = propertyGroup.Id, Name = "Content", SortOrder = 0 });
+ service.Save(page);
+
+ // Assert
+ Assert.That(compositionAdded, Is.True);
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(subtitleAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ }
+
+ [Test]
+ public void Can_Remove_PropertyGroup_On_Parent_Without_Causing_Duplicate_PropertyGroups()
+ {
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var basePage = MockedContentTypes.CreateBasicContentType();
+ service.Save(basePage);
+
+ var contentPage = MockedContentTypes.CreateBasicContentType("contentPage", "Content Page", basePage);
+ service.Save(contentPage);
+
+ var advancedPage = MockedContentTypes.CreateBasicContentType("advancedPage", "Advanced Page", contentPage);
+ service.Save(advancedPage);
+
+ var contentMetaComposition = MockedContentTypes.CreateContentMetaContentType();
+ service.Save(contentMetaComposition);
+
+ // Act
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
+ service.Save(basePage);
+
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
+ service.Save(contentPage);
+
+ var compositionAdded = contentPage.AddContentType(contentMetaComposition);
+ service.Save(contentPage);
+
+ basePage.RemovePropertyGroup("Content");
+ service.Save(basePage);
+
+ // Assert
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+ Assert.That(compositionAdded, Is.True);
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+
+ var contentType = service.GetContentType("contentPage");
+ var propertyGroup = contentType.PropertyGroups["Content"];
+ Assert.That(propertyGroup.ParentId.HasValue, Is.False);
+ }
+
+ [Test]
+ public void Can_Add_PropertyGroup_With_Same_Name_On_Parent_and_Child()
+ {
+ /*
+ * BasePage
+ * - Content Page
+ * -- Advanced Page
+ * Content Meta :: Composition
+ */
+
+ // Arrange
+ var service = ServiceContext.ContentTypeService;
+ var basePage = MockedContentTypes.CreateBasicContentType();
+ service.Save(basePage);
+
+ var contentPage = MockedContentTypes.CreateBasicContentType("contentPage", "Content Page", basePage);
+ service.Save(contentPage);
+
+ var advancedPage = MockedContentTypes.CreateBasicContentType("advancedPage", "Advanced Page", contentPage);
+ service.Save(advancedPage);
+
+ var contentMetaComposition = MockedContentTypes.CreateContentMetaContentType();
+ service.Save(contentMetaComposition);
+
+ // Act
+ var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
+ service.Save(contentPage);
+
+ var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
+ };
+ var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
+ service.Save(basePage);
+
+ var compositionAdded = contentPage.AddContentType(contentMetaComposition);
+ service.Save(contentPage);
+
+ // Assert
+ Assert.That(bodyTextAdded, Is.True);
+ Assert.That(authorAdded, Is.True);
+ Assert.That(compositionAdded, Is.True);
+
+ Assert.DoesNotThrow(() => service.GetContentType("contentPage"));
+ Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
+
+ var contentType = service.GetContentType("contentPage");
+ var propertyGroup = contentType.PropertyGroups["Content"];
+ Assert.That(propertyGroup.ParentId.HasValue, Is.False);
+
+ var numberOfContentTabs = contentType.CompositionPropertyGroups.Count(x => x.Name.Equals("Content"));
+ Assert.That(numberOfContentTabs, Is.EqualTo(3));
+
+ //Ensure that adding a new PropertyType to the "Content"-tab also adds it to the right group
+
+ var descriptionPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
+ {
+ Alias = "description", Name = "Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 1,DataTypeDefinitionId = -88
+ };
+ var descriptionAdded = contentType.AddPropertyType(descriptionPropertyType, "Content");
+ service.Save(contentType);
+ Assert.That(descriptionAdded, Is.True);
+
+ var contentPageReloaded = service.GetContentType("contentPage");
+ var propertyGroupReloaded = contentPageReloaded.PropertyGroups["Content"];
+ var hasDescriptionPropertyType = propertyGroupReloaded.PropertyTypes.Contains("description");
+ Assert.That(hasDescriptionPropertyType, Is.True);
+ Assert.That(propertyGroupReloaded.ParentId.HasValue, Is.False);
+
+ var descriptionPropertyTypeReloaded = propertyGroupReloaded.PropertyTypes["description"];
+ Assert.That(descriptionPropertyTypeReloaded.PropertyGroupId.IsValueCreated, Is.False);
}
private ContentType CreateComponent()
@@ -487,9 +1251,10 @@ namespace Umbraco.Tests.Services
private ContentType CreateBannerComponent(ContentType parent)
{
- var banner = new ContentType(parent)
+ const string contentTypeAlias = "banner";
+ var banner = new ContentType(parent, contentTypeAlias)
{
- Alias = "banner",
+ Alias = contentTypeAlias,
Name = "Banner Component",
Description = "ContentType used for Banner Component",
Icon = ".sprTreeDoc3",
@@ -535,9 +1300,10 @@ namespace Umbraco.Tests.Services
private ContentType CreateHomepage(ContentType parent)
{
- var contentType = new ContentType(parent)
+ const string contentTypeAlias = "homepage";
+ var contentType = new ContentType(parent, contentTypeAlias)
{
- Alias = "homepage",
+ Alias = contentTypeAlias,
Name = "Homepage",
Description = "ContentType used for the Homepage",
Icon = ".sprTreeDoc3",
@@ -571,7 +1337,7 @@ namespace Umbraco.Tests.Services
{
var contentType = MockedContentTypes.CreateSimpleContentType("childType" + i, "ChildType" + i,
//make the last entry in the list, this one's parent
- list.Last());
+ list.Last(), true);
list.Add(contentType);
}
diff --git a/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage-Random.xml b/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage-Random.xml
new file mode 100644
index 0000000000..517db639a0
--- /dev/null
+++ b/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage-Random.xml
@@ -0,0 +1,175 @@
+
+
+
+
+
+ Composite Test
+ dfsfd
+ MIT License
+ ddsff
+
+ 3
+ 0
+ 0
+
+
+
+ fsdfds
+ sfdf
+
+
+
+
+
+
+
+
+
+ Hello World
+ 0
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage.xml b/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage.xml
new file mode 100644
index 0000000000..f6b4e2c160
--- /dev/null
+++ b/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage.xml
@@ -0,0 +1,924 @@
+
+
+
+
+
+ Compositions Packaged
+ 1.0
+ MIT License
+ http://blog.sitereactor.dk
+
+ 3
+ 0
+ 0
+
+
+
+ Morten Christensen
+ http://blog.sitereactor.dk
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ 0
+
+
+
+ 1054
+
+
+ This is TXT, a free site template designed by n33 for HTML5 UP. It's built on the skelJS framework, uses well-thought out HTML5 and CSS3, and is fully responsive making it a great starting point for your own projects. And, like everything else we make, it's also free to use for pretty much any personal or commercial work under the Creative Commons Attribution license, so go ahead and use it – just don't forget to credit us!]]>
+
+
+
+
+ 0
+ 1
+
+
+ You've installed Umbraco and the "Txt" Starter Kit.
+
Edit the text on the homepage and create a site structure by adding new texpages to your site. This is all done in the Content section.
+
If you find the editing options provided by the Txt site too limited for you needs, simply add more properties to the page by going to the Settings section, expanding the Document Types item and adding new properties on the Generic Properties tab. You can find more information about document types and properties at the Umbraco website.
+
You'll probably want to personalize your site by changing the current design. This is also done in the Settings section, by editing the CSS styles and HTML templates. Umbraco uses master templates, so the main, common markup is placed in the Starterkit Master template, while the Homeage and Textpage have separate templates for their unique layouts. You can find more information about templates and css in umbraco at the umbraco website.
+
Once you're happy with your site's design, you might want to add more functionality, such as maps, image galleries or forms. This is done by installing Umbraco modules.
]]>
+
+
+
+ 0
+ 1
+
+
+ The Txt Starter Kit only scratches the surface of what's possible with Umbraco. Below the Txt Starter Kit and its modules lies a great architecture that lets you implement whatever you need.
+
With Umbraco you've finally got a solid, open and reliable platform for websites as basic as the Txt site, and Umbraco can be rapidly expanded to support multi-language websites, collaboration platforms and intra/extranets, to name just a few.
+
Advanced functionality is created with Umbraco macros, built with Umbraco's award-winning .NET integration, including full support for any .NET User or Custom control and ASP.NET MVC. Create and integrate your own .NET macros in mere minutes with point and click simplicity. Simply copy your controls to the Umbraco website, go to the Developer section and create a new macro, selecting your control from the list.
+
You can also use Microsoft's Razor syntax to quickly add dynamic functionality to your site.
+
We've also gathered the best community macros into a repository that's also accessed from the Developer section, in the Packages area. You can find more information about creating macros, on the Umbraco website.
+
The sky is the limit with Umbraco, and you have the benefit a friendly community, training, and guaranteed support. Find out how to get help.
]]>
+
+
+
+ 0
+ 1
+
+
+ Umbraco modules encapsulate specific bits of advanced functionality that are easily added to your website.
+
Once installed, Umbraco modules are open source and easy to customize if you want to modify the behavior for your specific needs.
+
Because Umbraco modules are provided under the MIT license you are free to use and modify them any way you want, with no strings attached.
+
To add Umbraco modules to your website, go to the Settings section, expand the Templates item, select the Starterkit Master template, then click the Customize Skin button on the toolbar.
+
Umbraco modules are available for various kinds of navigation, a sitemap, social media feeds, and a contact form. The list of available Umbraco modules is growing rapidly and is automatically updated from a central source, always fresh and current.
]]>
+
+
+
+ 0
+ 1
+
+
+ The Txt Starter Kit gives you a small website that introduces you to a set of well-defined conventions for building an Umbraco website.
+
Now that you know what the Txt Starter Kit is, it is time to get started using Umbraco.
]]>
+
+
+
+ 0
+
+
+ 0
+
+ Ita prorsus, inquam; Hanc ergo intuens debet institutum illud quasi signum absolvere. Ergo adhuc, quantum equidem intellego, causa non videtur fuisse mutandi nominis. Quia dolori non voluptas contraria est, sed doloris privatio. Nos autem non solum beatae vitae istam esse oblectationem videmus, sed etiam levamentum miseriarum. Quodsi ipsam honestatem undique pertectam atque absolutam. Nos cum te, M. Quod vestri non item.
+
Cum id quoque, ut cupiebat, audivisset, evelli iussit eam, qua erat transfixus, hastam. Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur. Quid iudicant sensus? Quo tandem modo?
]]>
+
+
+
+ 0
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Commoda autem et incommoda in eo genere sunt, quae praeposita et reiecta diximus; Bestiarum vero nullum iudicium puto. Est enim effectrix multarum et magnarum voluptatum. Duo Reges: constructio interrete. Claudii libidini, qui tum erat summo ne imperio, dederetur. Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur. Sed virtutem ipsam inchoavit, nihil amplius.
+
Ita redarguitur ipse a sese, convincunturque scripta eius probitate ipsius ac moribus. Istam voluptatem, inquit, Epicurus ignorat? Sed venio ad inconstantiae crimen, ne saepius dicas me aberrare; Sic, et quidem diligentius saepiusque ista loquemur inter nos agemusque communiter. Primum in nostrane potestate est, quid meminerimus? Consequens enim est et post oritur, ut dixi. Hoc mihi cum tuo fratre convenit. Immo videri fortasse. Itaque in rebus minime obscuris non multus est apud eos disserendi labor. Aliud igitur esse censet gaudere, aliud non dolere.
]]>
+
+
+
+ 0
+
+ Ut aliquid scire se gaudeant? Hanc ergo intuens debet institutum illud quasi signum absolvere. Vestri haec verecundius, illi fortasse constantius. Itaque sensibus rationem adiunxit et ratione effecta sensus non reliquit. Sed ea mala virtuti magnitudine obruebantur. Quasi ego id curem, quid ille aiat aut neget. Verum tamen cum de rebus grandioribus dicas, ipsae res verba rapiunt; Apparet statim, quae sint officia, quae actiones.
+
Virtutibus igitur rectissime mihi videris et ad consuetudinem nostrae orationis vitia posuisse contraria. Nonne videmus quanta perturbatio rerum omnium consequatur, quanta confusio? Sed eum qui audiebant, quoad poterant, defendebant sententiam suam. Ut necesse sit omnium rerum, quae natura vigeant, similem esse finem, non eundem.
]]>
+
+
+
+ 0
+ 2021-09-20T00:00:00
+
+
+
+ Sed ad haec, nisi molestum est, habeo quae velim. Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Facit igitur Lucius noster prudenter, qui audire de summo bono potissimum velit; Quis non odit sordidos, vanos, leves, futtiles?
+
Negat enim summo bono afferre incrementum diem. Haec mihi videtur delicatior, ut ita dicam, molliorque ratio, quam virtutis vis gravitasque postulat. Haec quo modo conveniant, non sane intellego. Quis enim confidit semper sibi illud stabile et firmum permansurum, quod fragile et caducum sit? Multoque hoc melius nos veriusque quam Stoici. Igitur neque stultorum quisquam beatus neque sapientium non beatus. De hominibus dici non necesse est. Non enim iam stirpis bonum quaeret, sed animalis. Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem. At, illa, ut vobis placet, partem quandam tuetur, reliquam deserit.
+
+
+ @foreach (var item in newsItems)
+ {
+
+ // If the editor has not explicitly provided the "Page title" property page
+ // then just show the name of the page otherwise show the provided title
+ var title = string.IsNullOrWhiteSpace(item.Title)
+ ? item.Name
+ : item.Title;
+
+
+ // If the editor has not explicitly set the publishDate property then show the create date
+ var dateTime = item.PublishDate == default(DateTime)
+ ? item.CreateDate
+ : item.PublishDate;
+
+
+
"
+ }
+ }
+ },
+ {
+ "value": "Sed porttitor lectus nibh. Nulla porttitor accumsan tincidunt. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Cras ultricies ligula sed magna dictum porta. Pellentesque in ipsum id orci porta dapibus. Pellentesque in ipsum id orci porta dapibus.",
+ "editor": {
+ "name": "Abstract",
+ "alias": "abstract",
+ "view": "textstring",
+ "icon": "icon-coin",
+ "config": {
+ "style": "font-size: 20px; line-height: 45px; font-weight: light",
+ "markup": "
#value#
"
+ }
+ }
+ },
+ {
+ "value": "Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Sed porttitor lectus nibh. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Pellentesque in ipsum id orci porta dapibus. Vivamus suscipit tortor eget felis porttitor volutpat. Vivamus suscipit tortor eget felis porttitor volutpat. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Curabitur aliquet quam id dui posuere blandit.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "
#value#
"
+ }
+ }
+ },
+ {
+ "value": "Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Sed porttitor lectus nibh. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Pellentesque in ipsum id orci porta dapibus. Vivamus suscipit tortor eget felis porttitor volutpat. Vivamus suscipit tortor eget felis porttitor volutpat. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Curabitur aliquet quam id dui posuere blandit.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "
"
+ }
+ }
+ },
+ {
+ "value": "Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Vivamus suscipit tortor eget felis porttitor volutpat. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Donec rutrum congue leo eget malesuada. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Nulla quis lorem ut libero malesuada feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula.",
+ "editor": {
+ "name": "Abstract",
+ "alias": "abstract",
+ "view": "textstring",
+ "icon": "icon-coin",
+ "config": {
+ "style": "font-size: 20px; line-height: 45px; font-weight: light",
+ "markup": "
#value#
"
+ }
+ }
+ },
+ {
+ "value": "Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Vivamus suscipit tortor eget felis porttitor volutpat. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Donec rutrum congue leo eget malesuada. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Nulla quis lorem ut libero malesuada feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "
#value#
"
+ }
+ }
+ },
+ {
+ "value": "Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Vivamus suscipit tortor eget felis porttitor volutpat. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Donec rutrum congue leo eget malesuada. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Nulla quis lorem ut libero malesuada feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "
#value#
"
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "styles": {},
+ "config": {
+ "class": "dark"
+ },
+ "id": "651b07fa-77eb-9f44-5633-fabbf5e365eb"
+ },
+ {
+ "name": "Playground",
+ "areas": [
+ {
+ "grid": 12,
+ "editors": [
+ "headline"
+ ],
+ "allowAll": false,
+ "controls": [
+ {
+ "value": "Donec rutrum congue leo eget malesuada. Sed porttitor lectus nibh. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Pellentesque in ipsum id orci porta dapibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque in ipsum id orci porta dapibus.",
+ "editor": {
+ "name": "Quote",
+ "alias": "quote",
+ "view": "textstring",
+ "icon": "icon-quote",
+ "config": {
+ "style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-variant: italic; font-size: 18px",
+ "markup": "
#value#
"
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "styles": {},
+ "config": {
+ "class": "yellow"
+ },
+ "id": "c9aabb6d-7787-ef69-f71c-a85c93c2f973"
+ },
+ {
+ "name": "Image wide",
+ "areas": [
+ {
+ "grid": 12,
+ "allowAll": false,
+ "allowed": [
+ "media_wide_cropped",
+ "media_wide"
+ ],
+ "controls": [
+ {
+ "value": {
+ "focalPoint": {
+ "left": 0.5,
+ "top": 0.5
+ },
+ "id": 1073,
+ "image": "/media/1005/54619133.jpg",
+ "thumbnail": "/umbraco/backoffice/UmbracoApi/Images/GetBigThumbnail?originalImagePath=%2Fmedia%2F1005%2F54619133.jpg"
+ },
+ "editor": {
+ "name": "Image wide",
+ "alias": "media_wide",
+ "view": "media",
+ "render": "/App_Plugins/Grid/Editors/Render/media_wide.cshtml",
+ "icon": "icon-picture"
+ }
+ }
+ ]
+ }
+ ],
+ "styles": {},
+ "config": {
+ "class": "full"
+ },
+ "id": "73807b0e-a282-c391-7e40-ccc7eb9a3472"
+ }
+ ]
+ }
+ ]
+}]]>
+
+
+
+
+ #value#"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "grid": 6,
+ "allowAll": false,
+ "allowed": [
+ "abstract",
+ "media",
+ "code",
+ "quote"
+ ],
+ "controls": [
+ {
+ "value": "What ever happens this should be the best thing you have ever encountered. We are not even beginning to scratch the surface. The comming months will prove that our path is the righteous one and all who oppose will burn on the bonfires of vanity.",
+ "editor": {
+ "name": "Abstract",
+ "alias": "abstract",
+ "view": "textstring",
+ "icon": "icon-coin",
+ "config": {
+ "style": "font-size: 20px; line-height: 45px; font-weight: light",
+ "markup": "
"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "grid": 6,
+ "allowAll": false,
+ "allowed": [
+ "abstract",
+ "media",
+ "code",
+ "quote",
+ "paragraph"
+ ],
+ "controls": [
+ {
+ "value": "Proin eget tortor risus. Sed porttitor lectus nibh. Proin eget tortor risus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Vivamus suscipit tortor eget felis porttitor volutpat. Donec rutrum congue leo eget malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.",
+ "editor": {
+ "name": "Abstract",
+ "alias": "abstract",
+ "view": "textstring",
+ "icon": "icon-coin",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: bold;",
+ "markup": "
#value#
"
+ }
+ }
+ },
+ {
+ "value": "Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Donec rutrum congue leo eget malesuada. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Vivamus suscipit tortor eget felis porttitor volutpat. Sed porttitor lectus nibh. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Nulla porttitor accumsan tincidunt. Donec rutrum congue leo eget malesuada.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "
#value#
"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "grid": 6,
+ "allowAll": false,
+ "allowed": [
+ "quote",
+ "code",
+ "media",
+ "paragraph"
+ ],
+ "controls": [
+ {
+ "value": "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Nulla porttitor accumsan tincidunt. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Proin eget tortor risus. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Quisque velit nisi, pretium ut lacinia in, elementum id enim.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "
"
+ }
+ }
+ },
+ {
+ "value": "Nulla porttitor accumsan tincidunt. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sollicitudin molestie malesuada. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ "editor": {
+ "name": "Abstract",
+ "alias": "abstract",
+ "view": "textstring",
+ "icon": "icon-coin",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: bold;",
+ "markup": "
#value#
"
+ }
+ }
+ },
+ {
+ "value": "Nulla quis lorem ut libero malesuada feugiat. Cras ultricies ligula sed magna dictum porta. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Donec sollicitudin molestie malesuada. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "
#value#
"
+ }
+ }
+ },
+ {
+ "value": "Pellentesque in ipsum id orci porta dapibus. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Nulla quis lorem ut libero malesuada feugiat. Nulla quis lorem ut libero malesuada feugiat.",
+ "editor": {
+ "name": "Paragraph",
+ "alias": "paragraph",
+ "view": "textstring",
+ "icon": "icon-font",
+ "config": {
+ "style": "font-size: 16px; line-height: 20px; font-weight: light;",
+ "markup": "