diff --git a/.gitignore b/.gitignore
index 04e39e37c9..543a67d7ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,133 +1,141 @@
-*.obj
-*.pdb
-*.user
-*.aps
-*.pch
-*.vspscc
-.DS_Store
-._.DS_Store
-[Bb]in
-[Db]ebug*/
-obj/
-[Rr]elease*/
-_ReSharper*/
-_NCrunch_*/
-*.ncrunchsolution
-*.ncrunchsolution.user
-*.ncrunchproject
-*.crunchsolution.cache
-[Tt]est[Rr]esult*
-[Bb]uild[Ll]og.*
-*.[Pp]ublish.xml
-*.suo
-[sS]ource
-[sS]andbox
-umbraco.config
-*.vs10x
-App_Data/TEMP/*
-[Uu]mbraco/[Pp]resentation/[Uu]mbraco/[Pp]lugins/*
-[Uu]mbraco/[Pp]resentation/[Uu]ser[Cc]ontrols/*
-[Uu]mbraco/[Pp]resentation/[Ss]cripts/*
-[Uu]mbraco/[Pp]resentation/[Ff]onts/*
-[Uu]mbraco/[Pp]resentation/[Cc]ss/*
-
-src/Umbraco.Web.UI/[Cc]ss/*
-src/Umbraco.Web.UI/App_Code/*
-src/Umbraco.Web.UI/App_Data/*
-src/Umbraco.Tests/App_Data/*
-src/Umbraco.Web.UI/[Mm]edia/*
-src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
-src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/*
-!src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/[Ww]eb.[Cc]onfig
-src/Umbraco.Web.UI/[Xx]slt/*
-src/Umbraco.Web.UI/[Ii]mages/*
-src/Umbraco.Web.UI/[Ss]cripts/*
-src/Umbraco.Web.UI/Web.*.config.transformed
-
-umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
-umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
-_BuildOutput/*
-*.ncrunchsolution
-build/UmbracoCms.AllBinaries*zip
-build/UmbracoCms.WebPI*zip
-build/UmbracoCms*zip
-build/UmbracoExamine.PDF*zip
-build/*.nupkg
-src/Umbraco.Tests/config/applications.config
-src/Umbraco.Tests/config/trees.config
-src/Umbraco.Web.UI/web.config
-*.orig
-src/Umbraco.Tests/config/404handlers.config
-src/Umbraco.Web.UI/[Vv]iews/*.cshtml
-src/Umbraco.Web.UI/[Vv]iews/*.vbhtml
-src/Umbraco.Tests/[Cc]onfig/umbracoSettings.config
-src/Umbraco.Web.UI/[Vv]iews/*
-src/packages/
-src/packages/repositories.config
-
-src/Umbraco.Web.UI/[Ww]eb.config
-*.transformed
-webpihash.txt
-
-node_modules
-
-src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.dev.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.dev.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
-
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.panel.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.palettes.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.loader.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.front.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.config.js
-
-src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
-src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
-src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
-src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
-src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
-src/Umbraco.Web.UI.Client/[Bb]uild/*
-src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
-src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
-build/_BuildOutput/
-src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
-tools/NDepend/
-
-src/Umbraco.Web.UI/App_Plugins/*
-src/*.psess
-src/*.vspx
-src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js.*
-NDependOut/*
-*.ndproj
-QueryResult.htm
-*.ndproj
-src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
-src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
-src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
-src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
-
-src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
-src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
-src/Umbraco.Web.UI/umbraco/plugins/*
-src/Umbraco.Web.UI/umbraco/js/init.js
-build/ApiDocs/*
-build/ApiDocs/Output/*
-src/Umbraco.Web.UI.Client/bower_components/*
-/src/Umbraco.Web.UI/Umbraco/preview
-
-#Ignore Rule for output of generated documentation files from Grunt docserve
-src/Umbraco.Web.UI.Client/docs/api
-src/*.boltdata/
-/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.loader.js
-/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.palettes.js
-/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.panel.js
-/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.config.js
-/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
-src/umbraco.sln.ide/*
-build/UmbracoCms.*/
-src/.vs/
+*.obj
+*.pdb
+*.user
+*.aps
+*.pch
+*.vspscc
+.DS_Store
+._.DS_Store
+[Bb]in
+[Db]ebug*/
+obj/
+[Rr]elease*/
+_ReSharper*/
+_NCrunch_*/
+*.ncrunchsolution
+*.ncrunchsolution.user
+*.ncrunchproject
+*.crunchsolution.cache
+[Tt]est[Rr]esult*
+[Bb]uild[Ll]og.*
+*.[Pp]ublish.xml
+*.suo
+[sS]ource
+[sS]andbox
+umbraco.config
+*.vs10x
+App_Data/TEMP/*
+[Uu]mbraco/[Pp]resentation/[Uu]mbraco/[Pp]lugins/*
+[Uu]mbraco/[Pp]resentation/[Uu]ser[Cc]ontrols/*
+[Uu]mbraco/[Pp]resentation/[Ss]cripts/*
+[Uu]mbraco/[Pp]resentation/[Ff]onts/*
+[Uu]mbraco/[Pp]resentation/[Cc]ss/*
+
+src/Umbraco.Web.UI/[Cc]ss/*
+src/Umbraco.Web.UI/App_Code/*
+src/Umbraco.Web.UI/App_Data/*
+src/Umbraco.Tests/App_Data/*
+src/Umbraco.Web.UI/[Mm]edia/*
+src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
+src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/*
+!src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/[Ww]eb.[Cc]onfig
+src/Umbraco.Web.UI/[Xx]slt/*
+src/Umbraco.Web.UI/[Ii]mages/*
+src/Umbraco.Web.UI/[Ss]cripts/*
+src/Umbraco.Web.UI/Web.*.config.transformed
+
+umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
+umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
+_BuildOutput/*
+*.ncrunchsolution
+build/UmbracoCms.AllBinaries*zip
+build/UmbracoCms.WebPI*zip
+build/UmbracoCms*zip
+build/UmbracoExamine.PDF*zip
+build/*.nupkg
+src/Umbraco.Tests/config/applications.config
+src/Umbraco.Tests/config/trees.config
+src/Umbraco.Web.UI/web.config
+*.orig
+src/Umbraco.Tests/config/404handlers.config
+src/Umbraco.Web.UI/[Vv]iews/*.cshtml
+src/Umbraco.Web.UI/[Vv]iews/*.vbhtml
+src/Umbraco.Tests/[Cc]onfig/umbracoSettings.config
+src/Umbraco.Web.UI/[Vv]iews/*
+src/packages/
+src/packages/repositories.config
+
+src/Umbraco.Web.UI/[Ww]eb.config
+*.transformed
+webpihash.txt
+
+node_modules
+
+src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.dev.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.dev.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
+
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.panel.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.palettes.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.loader.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.front.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.config.js
+
+src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
+src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
+src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
+src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
+src/Umbraco.Web.UI.Client/[Bb]uild/*
+src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
+src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
+build/_BuildOutput/
+src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
+tools/NDepend/
+
+src/Umbraco.Web.UI/App_Plugins/*
+src/*.psess
+src/*.vspx
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js.*
+NDependOut/*
+*.ndproj
+QueryResult.htm
+*.ndproj
+src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
+src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
+src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
+src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
+
+src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
+src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
+src/Umbraco.Web.UI/umbraco/plugins/*
+src/Umbraco.Web.UI/umbraco/js/init.js
+build/ApiDocs/*
+build/ApiDocs/Output/*
+src/Umbraco.Web.UI.Client/bower_components/*
+/src/Umbraco.Web.UI/Umbraco/preview
+
+#Ignore Rule for output of generated documentation files from Grunt docserve
+src/Umbraco.Web.UI.Client/docs/api
+src/*.boltdata/
+/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.loader.js
+/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.palettes.js
+/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.panel.js
+/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.config.js
+/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
+src/umbraco.sln.ide/*
+build/UmbracoCms.*/
+src/.vs/
+src/Umbraco.Web.UI/umbraco/js/install.loader.js
+src/Umbraco.Tests/media
+tools/docfx/*
+apidocs/_site/*
+apidocs/api/*
+build/docs.zip
+build/ui-docs.zip
+build/csharp-docs.zip
diff --git a/README.md b/README.md
index d4a024ddbc..53070b6917 100644
--- a/README.md
+++ b/README.md
@@ -7,19 +7,19 @@ The easiest way to get started is to run `build/build.bat` which will build both
If you're interested in making changes to Belle make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself.
-## Watch a five minute introduction video ##
+## Watch a introduction video ##
-[](http://umbraco.org/help-and-support/video-tutorials/getting-started/what-is-umbraco)
+[](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
## Umbraco - the simple, flexible and friendly ASP.NET CMS ##
-**More than 177,000 sites trust Umbraco**
+**More than 350,000 sites trust Umbraco**
-For the first time on the Microsoft platform a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, out of the box. It's a developers dream and your users will love it too.
+For the first time on the Microsoft platform, there is a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, out of the box. It's a developer's dream and your users will love it too.
-Used by more than 177,000 active websites including [http://daviscup.com](http://daviscup.com), [http://heinz.com](http://heinz.com), [http://peugeot.com](http://peugeot.com), [http://www.hersheys.com/](http://www.hersheys.com/) and **The Official ASP.NET and IIS.NET website from Microsoft** ([http://asp.net](http://asp.net) / [http://iis.net](http://iis.net)) you can be sure that the technology is proven, stable and scales.
+Used by more than 350,000 active websites including [http://daviscup.com](http://daviscup.com), [http://heinz.com](http://heinz.com), [http://peugeot.com](http://peugeot.com), [http://www.hersheys.com/](http://www.hersheys.com/) and **The Official ASP.NET and IIS.NET website from Microsoft** ([http://asp.net](http://asp.net) / [http://iis.net](http://iis.net)), you can be sure that the technology is proven, stable and scales.
-To view more examples please visit [http://umbraco.com/why-umbraco/#caseStudies](http://umbraco.com/why-umbraco/#caseStudies)
+To view more examples, please visit [http://umbraco.com/why-umbraco/#caseStudies](http://umbraco.com/why-umbraco/#caseStudies)
## Downloading ##
@@ -35,6 +35,6 @@ If you want to contribute back to Umbraco you should check out our [guide to con
## Found a bug? ##
-Another way you can contribute to Umbraco is by providing issue reports, for information on how to submit an issue report refer to our [online guide for reporting issues](http://our.umbraco.org/contribute/report-an-issue-or-request-a-feature).
+Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](http://our.umbraco.org/contribute/report-an-issue-or-request-a-feature).
-To view existing issues please visit [http://issues.umbraco.org](http://issues.umbraco.org)
+To view existing issues, please visit [http://issues.umbraco.org](http://issues.umbraco.org).
diff --git a/apidocs/docfx.filter.yml b/apidocs/docfx.filter.yml
new file mode 100644
index 0000000000..e96fbaafff
--- /dev/null
+++ b/apidocs/docfx.filter.yml
@@ -0,0 +1,18 @@
+apiRules:
+ - include:
+ uidRegex: ^Umbraco\.Core
+ - exclude:
+ uidRegex: ^umbraco\.Web\.org
+ - include:
+ uidRegex: ^Umbraco\.Web
+ - exclude:
+ hasAttribute:
+ uid: System.ComponentModel.EditorBrowsableAttribute
+ ctorArguments:
+ - System.ComponentModel.EditorBrowsableState.Never
+ - exclude:
+ uidRegex: ^umbraco\.
+ - exclude:
+ uidRegex: ^CookComputing\.
+ - exclude:
+ uidRegex: ^.*$
\ No newline at end of file
diff --git a/apidocs/docfx.json b/apidocs/docfx.json
new file mode 100644
index 0000000000..520622a0e0
--- /dev/null
+++ b/apidocs/docfx.json
@@ -0,0 +1,75 @@
+{
+ "metadata": [
+ {
+ "src": [
+ {
+ "files": [
+ "Umbraco.Core/Umbraco.Core.csproj",
+ "Umbraco.Web/Umbraco.Web.csproj"
+ ],
+ "exclude": [
+ "**/obj/**",
+ "**/bin/**",
+ "_site/**"
+ ],
+ "cwd": "../src"
+ }
+ ],
+ "dest": "../apidocs/api",
+ "filter": "../apidocs/docfx.filter.yml"
+ }
+ ],
+ "build": {
+ "content": [
+ {
+ "files": [
+ "api/**.yml",
+ "api/index.md"
+ ]
+ },
+ {
+ "files": [
+ "articles/**.md",
+ "articles/**/toc.yml",
+ "toc.yml",
+ "*.md"
+ ],
+ "exclude": [
+ "obj/**",
+ "_site/**"
+ ]
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "images/**"
+ ],
+ "exclude": [
+ "obj/**",
+ "_site/**"
+ ]
+ }
+ ],
+ "overwrite": [
+ {
+ "files": [
+ "**.md"
+ ],
+ "exclude": [
+ "obj/**",
+ "_site/**"
+ ]
+ }
+ ],
+ "globalMetadata": {
+ "_appTitle": "Umbraco c# Api docs",
+ "_enableSearch": true,
+ "_disableContribution": false
+ },
+ "dest": "_site",
+ "template": [
+ "default", "umbracotemplate"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/apidocs/index.md b/apidocs/index.md
new file mode 100644
index 0000000000..b90b816312
--- /dev/null
+++ b/apidocs/index.md
@@ -0,0 +1,7 @@
+
+# Umbraco c# API reference
+
+## Quick Links:
+
+### [Umbraco.Core](api/Umbraco.Core.html) docs
+### [Umbraco.Web](api/Umbraco.Web.html) docs
diff --git a/apidocs/toc.yml b/apidocs/toc.yml
new file mode 100644
index 0000000000..6817825066
--- /dev/null
+++ b/apidocs/toc.yml
@@ -0,0 +1,5 @@
+
+- name: Umbraco.Core Documentation
+ href: https://our.umbraco.org/apidocs/csharp/api/Umbraco.Core.html
+- name: Umbraco.Web Documentation
+ href: https://our.umbraco.org/apidocs/csharp/api/Umbraco.Web.html
\ No newline at end of file
diff --git a/apidocs/umbracotemplate/partials/class.tmpl.partial b/apidocs/umbracotemplate/partials/class.tmpl.partial
new file mode 100644
index 0000000000..9153a863a4
--- /dev/null
+++ b/apidocs/umbracotemplate/partials/class.tmpl.partial
@@ -0,0 +1,257 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+{{^_disableContribution}}
+{{#sourceurl}}{{__global.viewSource}} {{/sourceurl}}
+{{/_disableContribution}}
+
{{>partials/title}}
+{{{summary}}}
+{{{conceptual}}}
+{{#inheritance.0}}
+
+
{{__global.inheritance}}
+{{#inheritance}}
+
{{{specName.0.value}}}
+{{/inheritance}}
+
{{item.name.0.value}}
+
+{{/inheritance.0}}
+{{__global.namespace}} :{{namespace}}
+{{__global.assembly}} :{{assemblies.0}}.dll
+{{__global.syntax}}
+
+
{{syntax.content.0.value}}
+
+{{#syntax.parameters.0}}
+{{__global.parameters}}
+
+
+
+ {{__global.type}}
+ {{__global.name}}
+ {{__global.description}}
+
+
+
+{{/syntax.parameters.0}}
+{{#syntax.parameters}}
+
+ {{{type.specName.0.value}}}
+ {{{id}}}
+ {{{description}}}
+
+{{/syntax.parameters}}
+{{#syntax.parameters.0}}
+
+
+{{/syntax.parameters.0}}
+{{#syntax.return}}
+{{__global.returns}}
+
+
+
+ {{__global.type}}
+ {{__global.description}}
+
+
+
+
+ {{{type.specName.0.value}}}
+ {{{description}}}
+
+
+
+{{/syntax.return}}
+{{#syntax.typeParameters.0}}
+{{__global.typeParameters}}
+
+
+
+ {{__global.name}}
+ {{__global.description}}
+
+
+
+{{/syntax.typeParameters.0}}
+{{#syntax.typeParameters}}
+
+ {{{id}}}
+ {{{description}}}
+
+{{/syntax.typeParameters}}
+{{#syntax.typeParameters.0}}
+
+
+{{/syntax.typeParameters.0}}
+{{#remarks}}
+
+
+{{/remarks}}
+{{#example.0}}
+{{__global.examples}}
+{{/example.0}}
+{{#example}}
+{{{.}}}
+{{/example}}
+{{#children}}
+{{>partials/classSubtitle}}
+{{#children}}
+{{^_disableContribution}}
+{{#sourceurl}}
+
+ {{__global.viewSource}}
+ {{/sourceurl}}
+{{/_disableContribution}}
+{{name.0.value}}
+{{{summary}}}
+{{{conceptual}}}
+{{__global.declaration}}
+{{#syntax}}
+
+
{{syntax.content.0.value}}
+
+{{#parameters.0}}
+{{__global.parameters}}
+
+
+
+ {{__global.type}}
+ {{__global.name}}
+ {{__global.description}}
+
+
+
+{{/parameters.0}}
+{{#parameters}}
+
+ {{{type.specName.0.value}}}
+ {{{id}}}
+ {{{description}}}
+
+{{/parameters}}
+{{#parameters.0}}
+
+
+{{/parameters.0}}
+{{#return}}
+{{__global.returns}}
+
+
+
+ {{__global.type}}
+ {{__global.description}}
+
+
+
+
+ {{{type.specName.0.value}}}
+ {{{description}}}
+
+
+
+{{/return}}
+{{#typeParameters.0}}
+{{__global.typeParameters}}
+
+
+
+ {{__global.name}}
+ {{__global.description}}
+
+
+
+{{/typeParameters.0}}
+{{#typeParameters}}
+
+ {{{id}}}
+ {{{description}}}
+
+{{/typeParameters}}
+{{#typeParameters.0}}
+
+
+{{/typeParameters.0}}
+{{#fieldValue}}
+{{__global.fieldValue}}
+
+
+
+ {{__global.type}}
+ {{__global.description}}
+
+
+
+
+ {{{type.specName.0.value}}}
+ {{{description}}}
+
+
+
+{{/fieldValue}}
+{{#propertyValue}}
+{{__global.propertyValue}}
+
+
+
+ {{__global.type}}
+ {{__global.description}}
+
+
+
+
+ {{{type.specName.0.value}}}
+ {{{description}}}
+
+
+
+{{/propertyValue}}
+{{#eventType}}
+{{__global.eventType}}
+
+
+
+ {{__global.type}}
+ {{__global.description}}
+
+
+
+
+ {{{type.specName.0.value}}}
+ {{{description}}}
+
+
+
+{{/eventType}}
+{{/syntax}}
+{{#remarks}}
+
+
+{{/remarks}}
+{{#example.0}}
+{{__global.examples}}
+{{/example.0}}
+{{#example}}
+{{{.}}}
+{{/example}}
+{{#exceptions.0}}
+{{__global.exceptions}}
+
+
+
+ {{__global.type}}
+ {{__global.condition}}
+
+
+
+{{/exceptions.0}}
+{{#exceptions}}
+
+ {{{type.specName.0.value}}}
+ {{{description}}}
+
+{{/exceptions}}
+{{#exceptions.0}}
+
+
+{{/exceptions.0}}
+{{/children}}
+{{/children}}
diff --git a/apidocs/umbracotemplate/partials/footer.tmpl.partial b/apidocs/umbracotemplate/partials/footer.tmpl.partial
new file mode 100644
index 0000000000..69f51a101f
--- /dev/null
+++ b/apidocs/umbracotemplate/partials/footer.tmpl.partial
@@ -0,0 +1,13 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
diff --git a/apidocs/umbracotemplate/partials/head.tmpl.partial b/apidocs/umbracotemplate/partials/head.tmpl.partial
new file mode 100644
index 0000000000..591e1c1885
--- /dev/null
+++ b/apidocs/umbracotemplate/partials/head.tmpl.partial
@@ -0,0 +1,18 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+
+
+ {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}
+
+
+
+ {{#_description}} {{/_description}}
+
+
+
+
+
+
+ {{#_enableSearch}} {{/_enableSearch}}
+
diff --git a/apidocs/umbracotemplate/partials/namespace.tmpl.partial b/apidocs/umbracotemplate/partials/namespace.tmpl.partial
new file mode 100644
index 0000000000..80ca30799a
--- /dev/null
+++ b/apidocs/umbracotemplate/partials/namespace.tmpl.partial
@@ -0,0 +1,18 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+{{^_disableContribution}}
+{{#sourceurl}}
+{{__global.viewSource}}
+{{/sourceurl}}
+{{/_disableContribution}}
+{{>partials/title}}
+{{{summary}}}
+{{{conceptual}}}
+
+{{#children}}
+ {{>partials/namespaceSubtitle}}
+ {{#children}}
+ {{{specName.0.value}}}
+
+ {{/children}}
+{{/children}}
diff --git a/apidocs/umbracotemplate/partials/navbar.tmpl.partial b/apidocs/umbracotemplate/partials/navbar.tmpl.partial
new file mode 100644
index 0000000000..e9ee0af1c7
--- /dev/null
+++ b/apidocs/umbracotemplate/partials/navbar.tmpl.partial
@@ -0,0 +1,22 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+
+
diff --git a/apidocs/umbracotemplate/partials/rest.tmpl.partial b/apidocs/umbracotemplate/partials/rest.tmpl.partial
new file mode 100644
index 0000000000..4306bf7db1
--- /dev/null
+++ b/apidocs/umbracotemplate/partials/rest.tmpl.partial
@@ -0,0 +1,101 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+{{^_disableContribution}}
+{{#sourceurl}}View Source {{/sourceurl}}
+{{/_disableContribution}}
+{{name}}
+{{#summary}}
+{{{summary}}}
+{{/summary}}
+{{#description}}
+{{{description}}}
+{{/description}}
+{{#conceptual}}
+ {{{conceptual}}}
+{{/conceptual}}
+{{#children}}
+{{^_disableContribution}}
+{{#sourceurl}}
+
+ View Source
+ {{/sourceurl}}
+{{/_disableContribution}}
+ {{operationId}}
+{{#summary}}
+{{{summary}}}
+{{/summary}}
+{{#description}}
+{{{description}}}
+{{/description}}
+{{#conceptual}}
+ {{{conceptual}}}
+{{/conceptual}}
+Request
+
+
{{operation}} {{path}}
+
+{{#parameters.0}}
+Parameters
+
+
+
+ Name
+ Type
+ Value
+ Notes
+
+
+
+{{/parameters.0}}
+{{#parameters}}
+
+ {{#required}}*{{/required}}{{name}}
+ {{type}}
+ {{default}}
+ {{{description}}}
+
+ {{/parameters}}
+ {{#parameters.0}}
+
+
+{{/parameters.0}}
+{{#responses.0}}
+
+
Responses
+
+
+
+ Status Code
+ Description
+ Samples
+
+
+
+{{/responses.0}}
+{{#responses}}
+
+ {{statusCode}}
+ {{{description}}}
+
+ {{#examples}}
+
+ Mime type: {{mimeType}}
+
+ {{content}}
+ {{/examples}}
+
+
+ {{/responses}}
+ {{#responses.0}}
+
+
+
+{{/responses.0}}
+{{#footer}}
+
+{{/footer}}
+{{/children}}
+{{#footer}}
+
+{{/footer}}
+
diff --git a/apidocs/umbracotemplate/styles/main.css b/apidocs/umbracotemplate/styles/main.css
new file mode 100644
index 0000000000..7756b2f7d4
--- /dev/null
+++ b/apidocs/umbracotemplate/styles/main.css
@@ -0,0 +1,73 @@
+body {
+ color: rgba(0,0,0,.8);
+}
+.navbar-inverse {
+ background: #a3db78;
+}
+.navbar-inverse .navbar-nav>li>a, .navbar-inverse .navbar-text {
+ color: rgba(0,0,0,.8);
+}
+
+.navbar-inverse {
+ border-color: transparent;
+}
+
+.sidetoc {
+ background-color: #f5fbf1;
+}
+body .toc {
+ background-color: #f5fbf1;
+}
+.sidefilter {
+ background-color: #daf0c9;
+}
+.subnav {
+ background-color: #f5fbf1;
+}
+
+.navbar-inverse .navbar-nav>.active>a {
+ color: rgba(0,0,0,.8);
+ background-color: #daf0c9;
+}
+
+.navbar-inverse .navbar-nav>.active>a:focus, .navbar-inverse .navbar-nav>.active>a:hover {
+ color: rgba(0,0,0,.8);
+ background-color: #daf0c9;
+}
+
+.btn-primary {
+ color: rgba(0,0,0,.8);
+ background-color: #fff;
+ border-color: rgba(0,0,0,.8);
+}
+.btn-primary:hover {
+ background-color: #daf0c9;
+ color: rgba(0,0,0,.8);
+ border-color: rgba(0,0,0,.8);
+}
+
+.toc .nav > li > a {
+ color: rgba(0,0,0,.8);
+}
+
+button, a {
+ color: #f36f21;
+}
+
+button:hover,
+button:focus,
+a:hover,
+a:focus {
+ color: #143653;
+ text-decoration: none;
+}
+
+.navbar-header .navbar-brand {
+ background: url(https://our.umbraco.org/assets/images/logo.svg) left center no-repeat;
+ background-size: 40px auto;
+ width:50px;
+}
+
+.toc .nav > li.active > a {
+ color: #f36f21;
+}
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000000..db69da4978
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,51 @@
+version: '{build}'
+shallow_clone: true
+build_script:
+- cmd: >-
+ cd build
+
+ SET "release="
+
+ FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED release SET "release=%%i"
+
+ SET PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
+
+ ECHO %PATH%
+
+
+ ECHO Building Release %release% build%APPVEYOR_BUILD_NUMBER%
+
+
+ SET nuGetFolder=%CD%\..\src\packages\
+
+ ..\src\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/ >NUL
+
+ ..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
+
+
+ IF EXIST ..\src\umbraco.businesslogic\packages.config ..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
+
+ ..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
+
+
+ SET MSBUILD="C:\Program Files (x86)\MSBuild\14.0\Bin\MsBuild.exe"
+
+ %MSBUILD% "../src/Umbraco.Tests/Umbraco.Tests.csproj" /verbosity:minimal
+
+
+ build.bat %release% build%APPVEYOR_BUILD_NUMBER%
+
+
+ ECHO %PATH%
+test:
+ assemblies: src\Umbraco.Tests\bin\Debug\Umbraco.Tests.dll
+artifacts:
+- path: build\UmbracoCms.*
+notifications:
+- provider: Slack
+ auth_token:
+ secure: v2csJi2V5ghR0rPdODK8GJdOGNCA+XaK84iQ9MdPOClqB+VU+40ybdKp6gPirGSH
+ channel: '#build-umbraco-core'
+ on_build_success: false
+ on_build_failure: true
+ on_build_status_changed: false
\ No newline at end of file
diff --git a/build/ApiDocs/TOC.css b/build/ApiDocs/TOC.css
deleted file mode 100644
index 9c32aba214..0000000000
--- a/build/ApiDocs/TOC.css
+++ /dev/null
@@ -1,170 +0,0 @@
-/* File : TOC.css
-// Author : Eric Woodruff (Eric@EWoodruff.us)
-// Updated : 09/07/2007
-//
-// Stylesheet for the table of content
-*/
-
-*
-{
- margin: 0px 0px 0px 0px;
- padding: 0px 0px 0px 0px;
-}
-
-body
-{
- font-family: Segoe UI, Arial, Verdana, Helvetica, sans-serif;
- font-size: 0.9em;
- background-color: white;
- color: White;
- overflow: hidden;
-}
-
-input
-{
- padding:5px;
- margin: 3px 0px 3px 0px
-}
-
-img
-{
- border: 0;
- margin-left: 5px;
- margin-right: 2px;
-}
-
-img.TreeNodeImg
-{
- cursor: pointer;
-}
-
-img.TOCLink
-{
- cursor: pointer;
- margin-left: 0;
- margin-right: 0;
-}
-
-a.SelectedNode, a.UnselectedNode
-{
- color: #333;
- text-decoration: none;
- padding: 1px 3px 1px 3px;
- white-space: nowrap;
-}
-
-a.SelectedNode
-{
- background-color: #ffffff;
- border: solid 1px #999999;
- padding: 0px 2px 0px 2px;
-}
-
-a.UnselectedNode:hover, a.SelectedNode:hover
-{
- background-color: #cccccc;
- border: solid 1px #999999;
- padding: 0px 2px 0px 2px;
-}
-
-.Visible
-{
- display: block;
- margin-left: 2em;
-}
-
-.Hidden
-{
- display: none;
-}
-
-.Tree
-{
- background-color: #fff;
- color: #333;
- width: 300px;
- overflow: auto;
-}
-
-.TreeNode, .TreeItem
-{
- white-space: nowrap;
- margin: 2px 2px 2px 2px;
-}
-
-.TOCDiv
-{
- position: relative;
- float: left;
- width: 300px;
- height: 100%;
-}
-
-.TOCSizer
-{
- clear: none;
- float: left;
- width: 10px;
- height: 100%;
- background-image: url("Splitter.gif");
- background-position:center center;
- background-repeat:no-repeat;
- position: relative;
- cursor: w-resize;
- border-left: solid 1px #CCC;
-}
-
-.TopicContent
-{
- position: relative;
- float: right;
- background-color: white;
- height: 100%;
-}
-
-.SearchOpts
-{
- padding: 5px 5px 10px 5px;
- color: black;
- width: 300px;
- border-bottom: solid lightgrey 1px;
- height: 110px !important;
-}
-
-.NavOpts
-{
- padding: 5px 5px 6px 5px;
- color: black;
- width: 300px;
- border-bottom: solid lightgrey 1px;
-}
-
-.NavOpts img {
- display:inline-block;
- margin: 0px 5px 0px 5px;
-}
-
-.IndexOpts
-{
- padding: 5px 5px 6px 5px;
- color: black;
- width: 300px;
- border-bottom: solid lightgrey 1px;
-}
-
-.IndexItem
-{
- white-space: nowrap;
- margin: 2px 2px 2px 2px;
-}
-
-.IndexSubItem
-{
- white-space: nowrap;
- margin: 2px 2px 2px 12px;
-}
-
-.PaddedText
-{
- margin: 10px 10px 10px 10px;
-}
diff --git a/build/ApiDocs/csharp-api-docs.shfbproj b/build/ApiDocs/csharp-api-docs.shfbproj
deleted file mode 100644
index f635f2e2ee..0000000000
--- a/build/ApiDocs/csharp-api-docs.shfbproj
+++ /dev/null
@@ -1,172 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- 2.0
- {cb4d85f1-7390-40ee-b41f-a724bb8fddea}
- 1.9.5.0
-
- Documentation
- Documentation
- Documentation
-
- .NET Framework 4.5
- .\Output\
- UmbracoClassLibrary
- en-US
- OnlyErrors
- csharp-api-docs.log
- Website
- True
- False
- False
- False
- True
- CSharp
- Blank
- False
- VS2010
- False
- Guid
- Umbraco .Net Class Library
- AboveNamespaces
- Attributes, InheritedMembers, InheritedFrameworkMembers, Protected, SealedProtected
-
-
-
-
-
-
- None
- None
- False
- True
- Summary, AutoDocumentCtors, AutoDocumentDispose
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/Build.bat b/build/Build.bat
index bd18f719ad..1167d9fcb6 100644
--- a/build/Build.bat
+++ b/build/Build.bat
@@ -21,14 +21,6 @@ ECHO Building Umbraco %version%
ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %release%
-ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise you'd have to run build.cmd twice
-SET nuGetFolder=%CD%\..\src\packages\
-..\src\.nuget\NuGet.exe sources Remove -Name MyGetUmbracoCore >NUL
-..\src\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/ >NUL
-..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
-..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
-..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
-
ECHO Removing the belle build folder and bower_components folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
RD ..\src\Umbraco.Web.UI.Client\bower_components /Q /S
diff --git a/build/Build.proj b/build/Build.proj
index e937df15bd..9413b49d15 100644
--- a/build/Build.proj
+++ b/build/Build.proj
@@ -175,8 +175,8 @@
diff --git a/build/BuildBelle.bat b/build/BuildBelle.bat
index 0d58204f65..6c11cc9fc5 100644
--- a/build/BuildBelle.bat
+++ b/build/BuildBelle.bat
@@ -1,4 +1,6 @@
@ECHO OFF
+SETLOCAL
+
SET release=%1
ECHO Installing Npm NuGet Package
@@ -11,12 +13,9 @@ ECHO Current folder: %CD%
for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
-ECHO Temporarily adding Npm and Node to path
-SET oldPath=%PATH%
-
-path=%npmPath%;%nodePath%;%PATH%
-
-ECHO %path%
+ECHO Adding Npm and Node to path
+REM SETLOCAL is on, so changes to the path not persist to the actual user's path
+PATH=%npmPath%;%nodePath%;%PATH%
SET buildFolder=%CD%
@@ -29,8 +28,5 @@ call npm install -g grunt-cli --quiet
call npm install -g bower --quiet
call grunt build --buildversion=%release%
-ECHO Reset path to what it was before
-path=%oldPath%
-
ECHO Move back to the build folder
CD %buildFolder%
\ No newline at end of file
diff --git a/build/BuildDocs.bat b/build/BuildDocs.bat
new file mode 100644
index 0000000000..9d0a04e1cd
--- /dev/null
+++ b/build/BuildDocs.bat
@@ -0,0 +1,20 @@
+@ECHO OFF
+SETLOCAL
+
+SET release=%1
+ECHO Installing Npm NuGet Package
+
+SET nuGetFolder=%CD%\..\src\packages\
+ECHO Configured packages folder: %nuGetFolder%
+ECHO Current folder: %CD%
+
+%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% -Verbosity quiet
+
+for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
+for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
+
+ECHO Adding Npm and Node to path
+REM SETLOCAL is on, so changes to the path not persist to the actual user's path
+PATH=%npmPath%;%nodePath%;%PATH%
+
+Powershell.exe -ExecutionPolicy Unrestricted -File .\BuildDocs.ps1
\ No newline at end of file
diff --git a/build/BuildDocs.ps1 b/build/BuildDocs.ps1
index 6f46a43fde..dcb3a85cc1 100644
--- a/build/BuildDocs.ps1
+++ b/build/BuildDocs.ps1
@@ -1,27 +1,100 @@
-##We cannot continue if sandcastle is not installed determined by env variable: SHFBROOT
+$PSScriptFilePath = (Get-Item $MyInvocation.MyCommand.Path);
+$RepoRoot = (get-item $PSScriptFilePath).Directory.Parent.FullName;
+$SolutionRoot = Join-Path -Path $RepoRoot "src";
+$ToolsRoot = Join-Path -Path $RepoRoot "tools";
+$DocFx = Join-Path -Path $ToolsRoot "docfx\docfx.exe"
+$DocFxFolder = (Join-Path -Path $ToolsRoot "docfx")
+$DocFxJson = Join-Path -Path $RepoRoot "apidocs\docfx.json"
+$7Zip = Join-Path -Path $ToolsRoot "7zip\7za.exe"
+$DocFxSiteOutput = Join-Path -Path $RepoRoot "apidocs\_site\*.*"
+$NgDocsSiteOutput = Join-Path -Path $RepoRoot "src\Umbraco.Web.UI.Client\docs\api\*.*"
+$ProgFiles86 = [Environment]::GetEnvironmentVariable("ProgramFiles(x86)");
+$MSBuild = "$ProgFiles86\MSBuild\14.0\Bin\MSBuild.exe"
-if (-not (Test-Path Env:\SHFBROOT))
-{
- throw "The docs cannot be build, install Sandcastle help file builder"
+
+################ Do the UI docs
+
+"Changing to Umbraco.Web.UI.Client folder"
+cd ..
+cd src\Umbraco.Web.UI.Client
+Write-Host $(Get-Location)
+
+"Creating build folder so MSBuild doesn't run the whole grunt build"
+if (-Not (Test-Path "build")) {
+ md "build"
}
-$PSScriptFilePath = (Get-Item $MyInvocation.MyCommand.Path).FullName
-$BuildRoot = Split-Path -Path $PSScriptFilePath -Parent
-$OutputPath = Join-Path -Path $BuildRoot -ChildPath "ApiDocs\Output"
-$ProjFile = Join-Path -Path $BuildRoot -ChildPath "ApiDocs\csharp-api-docs.shfbproj"
+"Installing node"
+# Check if Install-Product exists, should only exist on the build server
+if (Get-Command Install-Product -errorAction SilentlyContinue)
+{
+ Install-Product node ''
+}
-"Building docs with project file: $ProjFile"
+"Installing node modules"
+& npm install
-$MSBuild = "$Env:SYSTEMROOT\Microsoft.NET\Framework\v4.0.30319\msbuild.exe"
+"Installing grunt"
+& npm install -g grunt-cli
-# build it!
-& $MSBuild "$ProjFile"
+"Moving back to build folder"
+cd ..
+cd ..
+cd build
+Write-Host $(Get-Location)
-# remove files left over
-Remove-Item $BuildRoot\* -include csharp-api-docs.shfbproj_*
+ & grunt --gruntfile ../src/umbraco.web.ui.client/gruntfile.js docs
-# copy our custom styles in
-Copy-Item $BuildRoot\ApiDocs\TOC.css $OutputPath\TOC.css
+# change baseUrl
+$BaseUrl = "https://our.umbraco.org/apidocs/ui/"
+$IndexPath = "../src/umbraco.web.ui.client/docs/api/index.html"
+(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
+# zip it
-""
-"Done!"
\ No newline at end of file
+& $7Zip a -tzip ui-docs.zip $NgDocsSiteOutput -r
+
+################ Do the c# docs
+
+# Build the solution in debug mode
+$SolutionPath = Join-Path -Path $SolutionRoot -ChildPath "umbraco.sln"
+& $MSBuild "$SolutionPath" /p:Configuration=Debug /maxcpucount /t:Clean
+if (-not $?)
+{
+ throw "The MSBuild process returned an error code."
+}
+& $MSBuild "$SolutionPath" /p:Configuration=Debug /maxcpucount
+if (-not $?)
+{
+ throw "The MSBuild process returned an error code."
+}
+
+# Go get docfx if we don't hae it
+$FileExists = Test-Path $DocFx
+If ($FileExists -eq $False) {
+
+ If(!(Test-Path $DocFxFolder))
+ {
+ New-Item $DocFxFolder -type directory
+ }
+
+ $DocFxZip = Join-Path -Path $ToolsRoot "docfx\docfx.zip"
+ $DocFxSource = "https://github.com/dotnet/docfx/releases/download/v1.9.4/docfx.zip"
+ Invoke-WebRequest $DocFxSource -OutFile $DocFxZip
+
+ #unzip it
+ & $7Zip e $DocFxZip "-o$DocFxFolder"
+}
+
+#clear site
+If(Test-Path(Join-Path -Path $RepoRoot "apidocs\_site"))
+{
+ Remove-Item $DocFxSiteOutput -recurse
+}
+
+# run it!
+& $DocFx metadata $DocFxJson
+& $DocFx build $DocFxJson
+
+# zip it
+
+& $7Zip a -tzip csharp-docs.zip $DocFxSiteOutput -r
diff --git a/build/InstallGit.cmd b/build/InstallGit.cmd
index f10fe6c100..b6ba71df9b 100644
--- a/build/InstallGit.cmd
+++ b/build/InstallGit.cmd
@@ -1,19 +1,19 @@
@ECHO OFF
-SET oldPath=%PATH%
+SETLOCAL
+REM SETLOCAL is on, so changes to the path not persist to the actual user's path
git.exe 2> NUL
if %ERRORLEVEL%==9009 GOTO :trydefaultpath
GOTO :EOF
:trydefaultpath
-path=C:\Program Files (x86)\Git\cmd;%PATH%
+path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
git.exe 2> NUL
if %ERRORLEVEL%==9009 GOTO :showerror
GOTO :EOF
:showerror
-path=%oldPath%
-ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\bin
+ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\cmd nor in C:\Program Files\Git\cmd
set /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
if %install%==y (
GOTO :installgit
@@ -29,5 +29,4 @@ GOTO :EOF
ECHO Installing Chocolatey first
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
ECHO Installing Git through Chocolatey
-choco install git
-path=C:\Program Files (x86)\Git\cmd;%path%
+choco install git
\ No newline at end of file
diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec
index 0c7d51334e..72733c5800 100644
--- a/build/NuSpecs/UmbracoCms.Core.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.nuspec
@@ -17,27 +17,26 @@
-
-
+
-
+
-
+
-
-
+
+
-
-
+
+
@@ -72,35 +71,34 @@
-
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
\ No newline at end of file
diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index 61a8188804..66e82ac488 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -16,7 +16,8 @@
umbraco
-
+
+
diff --git a/build/NuSpecs/build/UmbracoCms.targets b/build/NuSpecs/build/UmbracoCms.targets
index 024d8af7ad..fde5b4ea81 100644
--- a/build/NuSpecs/build/UmbracoCms.targets
+++ b/build/NuSpecs/build/UmbracoCms.targets
@@ -1,6 +1,17 @@
+
+
+ $(MSBuildThisFileDirectory)..\UmbracoFiles\
+
+
+
+
+
+
+
+
$(MSBuildThisFileDirectory)..\UmbracoFiles\
@@ -47,4 +58,4 @@
-
\ 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 e4d621f693..a77632926c 100644
--- a/build/NuSpecs/tools/Dashboard.config.install.xdt
+++ b/build/NuSpecs/tools/Dashboard.config.install.xdt
@@ -1,6 +1,6 @@
-
+
-
+
views/dashboard/developer/developerdashboardvideos.html
@@ -29,7 +29,7 @@
-
+
views/dashboard/developer/examinemanagement.html
@@ -39,10 +39,10 @@
views/dashboard/developer/xmldataintegrityreport.html
-
+
-
+
-
+
@@ -62,17 +62,10 @@
views/dashboard/default/startupdashboardintro.html
-
-
-
-
- views/dashboard/ChangePassword.html
-
-
-
\ No newline at end of file
diff --git a/build/NuSpecs/tools/Readme.txt b/build/NuSpecs/tools/Readme.txt
index 53d9c3c7da..b6b55c1c4f 100644
--- a/build/NuSpecs/tools/Readme.txt
+++ b/build/NuSpecs/tools/Readme.txt
@@ -10,6 +10,7 @@
Don't forget to build!
+
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).
diff --git a/build/NuSpecs/tools/ReadmeUpgrade.txt b/build/NuSpecs/tools/ReadmeUpgrade.txt
index 29aa41b55a..e0d660a795 100644
--- a/build/NuSpecs/tools/ReadmeUpgrade.txt
+++ b/build/NuSpecs/tools/ReadmeUpgrade.txt
@@ -10,8 +10,6 @@
Don't forget to build!
-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 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.
diff --git a/build/NuSpecs/tools/Views.Web.config.install.xdt b/build/NuSpecs/tools/Views.Web.config.install.xdt
index c34963f2b0..a5797232b8 100644
--- a/build/NuSpecs/tools/Views.Web.config.install.xdt
+++ b/build/NuSpecs/tools/Views.Web.config.install.xdt
@@ -17,6 +17,11 @@
+
+
+
+
+
diff --git a/build/NuSpecs/tools/Web.config.install.xdt b/build/NuSpecs/tools/Web.config.install.xdt
index f7fbad09bd..dfb9925840 100644
--- a/build/NuSpecs/tools/Web.config.install.xdt
+++ b/build/NuSpecs/tools/Web.config.install.xdt
@@ -7,8 +7,8 @@
-
-
+
+
@@ -21,8 +21,10 @@
+
+
-
+
@@ -37,7 +39,7 @@
-
+
@@ -48,8 +50,15 @@
+
+
+
+
+
-
+
+
+
>
@@ -58,9 +67,9 @@
-
+
+
@@ -72,13 +81,171 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -113,6 +280,8 @@
+
+
@@ -131,20 +300,27 @@
-
-
+
+
+
+
+
+
+
-
+
+
+
@@ -156,6 +332,10 @@
+
+
+
+
@@ -166,29 +346,33 @@
-
+
+
+
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -196,19 +380,19 @@
-
+
-
+
-
+
-
+
-
+
\ No newline at end of file
diff --git a/build/NuSpecs/tools/install.core.ps1 b/build/NuSpecs/tools/install.core.ps1
index 1fa9352be7..c4f213ba01 100644
--- a/build/NuSpecs/tools/install.core.ps1
+++ b/build/NuSpecs/tools/install.core.ps1
@@ -1,18 +1,30 @@
-param($rootPath, $toolsPath, $package, $project)
+param($installPath, $toolsPath, $package, $project)
+
+Write-Host "installPath:" "${installPath}"
+Write-Host "toolsPath:" "${toolsPath}"
+
+Write-Host " "
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
- $backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
+
+ # Create paths and list them
+ $projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName
+ Write-Host "projectPath:" "${projectPath}"
+ $backupPath = Join-Path $projectPath "App_Data\NuGetBackup\$dateTime"
+ Write-Host "backupPath:" "${backupPath}"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
- $projectDestinationPath = Split-Path $project.FullName -Parent
-
+ Write-Host "copyLogsPath:" "${copyLogsPath}"
+ $umbracoBinFolder = Join-Path $projectPath "bin"
+ Write-Host "umbracoBinFolder:" "${umbracoBinFolder}"
+
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# After backing up, remove all umbraco dlls from bin folder in case dll files are included in the VS project
# See: http://issues.umbraco.org/issue/U4-4930
- $umbracoBinFolder = Join-Path $projectDestinationPath "bin"
+
if(Test-Path $umbracoBinFolder) {
$umbracoBinBackupPath = Join-Path $backupPath "bin"
@@ -20,7 +32,7 @@ if ($project) {
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
- # Delete files Umbraco brings in
+ # Delete files Umbraco ships with
if(Test-Path $umbracoBinFolder\businesslogic.dll) { Remove-Item $umbracoBinFolder\businesslogic.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\cms.dll) { Remove-Item $umbracoBinFolder\cms.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\controls.dll) { Remove-Item $umbracoBinFolder\controls.dll -Force -Confirm:$false }
@@ -36,16 +48,18 @@ if ($project) {
if(Test-Path $umbracoBinFolder\umbraco.DataLayer.dll) { Remove-Item $umbracoBinFolder\umbraco.DataLayer.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.editorControls.dll) { Remove-Item $umbracoBinFolder\umbraco.editorControls.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.MacroEngines.dll) { Remove-Item $umbracoBinFolder\umbraco.MacroEngines.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Umbraco.ModelsBuilder.dll) { Remove-Item $umbracoBinFolder\Umbraco.ModelsBuilder.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Umbraco.ModelsBuilder.AspNet.dll) { Remove-Item $umbracoBinFolder\Umbraco.ModelsBuilder.AspNet.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.providers.dll) { Remove-Item $umbracoBinFolder\umbraco.providers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.Web.UI.dll) { Remove-Item $umbracoBinFolder\Umbraco.Web.UI.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\UmbracoExamine.dll) { Remove-Item $umbracoBinFolder\UmbracoExamine.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\UrlRewritingNet.UrlRewriter.dll) { Remove-Item $umbracoBinFolder\UrlRewritingNet.UrlRewriter.dll -Force -Confirm:$false }
# Delete files Umbraco depends upon
- $amd64Folder = Join-Path $projectDestinationPath "bin\amd64"
+ $amd64Folder = Join-Path $umbracoBinFolder "amd64"
if(Test-Path $amd64Folder) { Remove-Item $amd64Folder -Force -Recurse -Confirm:$false }
- $x86Folder = Join-Path $projectDestinationPath "bin\x86"
- if(Test-Path $x86Folder) { Remove-Item $x86Folder -Force -Recurse -Confirm:$false }
+ $x86Folder = Join-Path $umbracoBinFolder "x86"
+ if(Test-Path $x86Folder) { Remove-Item $x86Folder -Force -Recurse -Confirm:$false }
if(Test-Path $umbracoBinFolder\AutoMapper.dll) { Remove-Item $umbracoBinFolder\AutoMapper.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\AutoMapper.Net4.dll) { Remove-Item $umbracoBinFolder\AutoMapper.Net4.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ClientDependency.Core.dll) { Remove-Item $umbracoBinFolder\ClientDependency.Core.dll -Force -Confirm:$false }
@@ -57,19 +71,35 @@ if ($project) {
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Infrastructure.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Infrastructure.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Owin.dll) { Remove-Item $umbracoBinFolder\Owin.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Semver.dll) { Remove-Item $umbracoBinFolder\Semver.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Collections.Immutable.dll) { Remove-Item $umbracoBinFolder\System.Collections.Immutable.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Reflection.Metadata.dll) { Remove-Item $umbracoBinFolder\System.Reflection.Metadata.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Net.Http.Extensions.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Extensions.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Net.Http.Primitives.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Primitives.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.Razor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.WebPages.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.WebPages.Deployment.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Deployment.dll -Force -Confirm:$false }
- if(Test-Path $umbracoBinFolder\System.Web.WebPages.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Razor.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Web.WebPages.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Razor.dll -Force -Confirm:$false }
}
}
\ No newline at end of file
diff --git a/build/NuSpecs/tools/install.ps1 b/build/NuSpecs/tools/install.ps1
index 49b49f6cfe..5ebaa6d02a 100644
--- a/build/NuSpecs/tools/install.ps1
+++ b/build/NuSpecs/tools/install.ps1
@@ -1,21 +1,33 @@
-param($rootPath, $toolsPath, $package, $project)
+param($installPath, $toolsPath, $package, $project)
+
+Write-Host "installPath:" "${installPath}"
+Write-Host "toolsPath:" "${toolsPath}"
+
+Write-Host " "
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
- $backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
+
+ # Create paths and list them
+ $projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName
+ Write-Host "projectPath:" "${projectPath}"
+ $backupPath = Join-Path $projectPath "App_Data\NuGetBackup\$dateTime"
+ Write-Host "backupPath:" "${backupPath}"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
- $projectDestinationPath = Split-Path $project.FullName -Parent
+ Write-Host "copyLogsPath:" "${copyLogsPath}"
+ $webConfigSource = Join-Path $projectPath "Web.config"
+ Write-Host "webConfigSource:" "${webConfigSource}"
+ $configFolder = Join-Path $projectPath "Config"
+ Write-Host "configFolder:" "${configFolder}"
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# Create a backup of original web.config
- $webConfigSource = Join-Path $projectDestinationPath "Web.config"
Copy-Item $webConfigSource $backupPath -Force
- # Backup config files folder
- $configFolder = Join-Path $projectDestinationPath "Config"
+ # Backup config files folder
if(Test-Path $configFolder) {
$umbracoBackupPath = Join-Path $backupPath "Config"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
@@ -24,32 +36,24 @@ if ($project) {
}
# Copy umbraco and umbraco_files from package to project folder
- # This is only done when these folders already exist because we
- # only want to do this for upgrades
- $umbracoFolder = Join-Path $projectDestinationPath "Umbraco"
- if(Test-Path $umbracoFolder) {
- $umbracoFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco"
-
- $umbracoBackupPath = Join-Path $backupPath "Umbraco"
- New-Item -ItemType Directory -Force -Path $umbracoBackupPath
-
- robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
- robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
- }
+ $umbracoFolder = Join-Path $projectPath "Umbraco"
+ New-Item -ItemType Directory -Force -Path $umbracoFolder
+ $umbracoFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco"
+ $umbracoBackupPath = Join-Path $backupPath "Umbraco"
+ New-Item -ItemType Directory -Force -Path $umbracoBackupPath
+ robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
+ robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
- $umbracoClientFolder = Join-Path $projectDestinationPath "Umbraco_Client"
- if(Test-Path $umbracoClientFolder) {
- $umbracoClientFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco_Client"
-
- $umbracoClientBackupPath = Join-Path $backupPath "Umbraco_Client"
- New-Item -ItemType Directory -Force -Path $umbracoClientBackupPath
-
- robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
- robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
- }
+ $umbracoClientFolder = Join-Path $projectPath "Umbraco_Client"
+ New-Item -ItemType Directory -Force -Path $umbracoClientFolder
+ $umbracoClientFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco_Client"
+ $umbracoClientBackupPath = Join-Path $backupPath "Umbraco_Client"
+ New-Item -ItemType Directory -Force -Path $umbracoClientBackupPath
+ robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
+ robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
$copyWebconfig = $true
- $destinationWebConfig = Join-Path $projectDestinationPath "Web.config"
+ $destinationWebConfig = Join-Path $projectPath "Web.config"
if(Test-Path $destinationWebConfig)
{
@@ -71,11 +75,31 @@ if ($project) {
if($copyWebconfig -eq $true)
{
- $packageWebConfigSource = Join-Path $rootPath "UmbracoFiles\Web.config"
+ $packageWebConfigSource = Join-Path $installPath "UmbracoFiles\Web.config"
Copy-Item $packageWebConfigSource $destinationWebConfig -Force
- }
- $installFolder = Join-Path $projectDestinationPath "Install"
+ # Copy files that don't get automatically copied for Website projects
+ # We do this here, when copyWebconfig is true because we only want to do it for new installs
+ # If this is an upgrade then the files should already be there
+ $splashesSource = Join-Path $installPath "UmbracoFiles\Config\splashes\*.*"
+ $splashesDestination = Join-Path $projectPath "Config\splashes\"
+ New-Item $splashesDestination -Type directory
+ Copy-Item $splashesSource $splashesDestination -Force
+
+ $sqlCe64Source = Join-Path $installPath "UmbracoFiles\bin\amd64\*"
+ $sqlCe64Destination = Join-Path $projectPath "bin\amd64\"
+ Copy-Item $sqlCe64Source $sqlCe64Destination -Force
+
+ $sqlCex86Source = Join-Path $installPath "UmbracoFiles\bin\x86\*"
+ $sqlCex86Destination = Join-Path $projectPath "bin\x86\"
+ Copy-Item $sqlCex86source $sqlCex86Destination -Force
+
+ $umbracoUIXMLSource = Join-Path $installPath "UmbracoFiles\Umbraco\Config\Create\UI.xml"
+ $umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
+ Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
+ }
+
+ $installFolder = Join-Path $projectPath "Install"
if(Test-Path $installFolder) {
Remove-Item $installFolder -Force -Recurse -Confirm:$false
}
diff --git a/build/NuSpecs/tools/trees.config.install.xdt b/build/NuSpecs/tools/trees.config.install.xdt
index 45f43206fe..580c619547 100644
--- a/build/NuSpecs/tools/trees.config.install.xdt
+++ b/build/NuSpecs/tools/trees.config.install.xdt
@@ -1,36 +1,127 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/RevertToCleanInstall.bat b/build/RevertToCleanInstall.bat
index 6c391386cd..b21b33d8ff 100644
--- a/build/RevertToCleanInstall.bat
+++ b/build/RevertToCleanInstall.bat
@@ -45,9 +45,6 @@ del ..\src\Umbraco.Web.UI\UserControls\*.*
echo Removing masterpage files
del ..\src\Umbraco.Web.UI\masterpages\*.*
-echo Removing view files
-del ..\src\Umbraco.Web.UI\Views\*.*
-
echo Removing razor files
del ..\src\Umbraco.Web.UI\macroScripts\*.*
@@ -104,7 +101,9 @@ echo Removing user control files
FOR %%A IN (..\src\Umbraco.Web.UI\usercontrols\*.*) DO DEL %%A
echo Removing view files
-FOR %%A IN (..\src\Umbraco.Web.UI\Views\*.*) DO DEL %%A
+ATTRIB +H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
+FOR %%A IN (..\src\Umbraco.Web.UI\Views\) DO DEL /Q /S *.cshtml -H
+ATTRIB -H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
echo Removing razor files
FOR %%A IN (..\src\Umbraco.Web.UI\macroScripts\*.*) DO DEL %%A
diff --git a/build/RevertToEmptyInstall.bat b/build/RevertToEmptyInstall.bat
index d59e2cdc88..b8abe4e64e 100644
--- a/build/RevertToEmptyInstall.bat
+++ b/build/RevertToEmptyInstall.bat
@@ -54,9 +54,6 @@ del ..\src\Umbraco.Web.UI\UserControls\*.*
echo Removing masterpage files
del ..\src\Umbraco.Web.UI\masterpages\*.*
-echo Removing view files
-del ..\src\Umbraco.Web.UI\Views\*.*
-
echo Removing razor files
del ..\src\Umbraco.Web.UI\macroScripts\*.*
@@ -122,7 +119,9 @@ echo Removing user control files
FOR %%A IN (..\src\Umbraco.Web.UI\usercontrols\*.*) DO DEL %%A
echo Removing view files
-FOR %%A IN (..\src\Umbraco.Web.UI\Views\*.*) DO DEL %%A
+ATTRIB +H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
+FOR %%A IN (..\src\Umbraco.Web.UI\Views\) DO DEL /Q /S *.cshtml -H
+ATTRIB -H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
echo Removing razor files
FOR %%A IN (..\src\Umbraco.Web.UI\macroScripts\*.*) DO DEL %%A
@@ -151,6 +150,9 @@ rmdir "..\src\Umbraco.Web.UI\usercontrols\umbracoContour\" /S /Q
echo Start with a clean web.config
copy ..\src\Umbraco.Web.UI\web.Template.config ..\src\Umbraco.Web.UI\web.config /Y
+echo Start with a clean web.config
+copy ..\src\Umbraco.Web.UI\web.Template.config ..\src\Umbraco.Web.UI\web.config /Y
+
echo "Umbraco install reverted to clean install"
pause
exit
diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt
index ed2ae80c6e..4b66ecc866 100644
--- a/build/UmbracoVersion.txt
+++ b/build/UmbracoVersion.txt
@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
-7.3.0
\ No newline at end of file
+7.5.0
\ No newline at end of file
diff --git a/src/NuGet.Config b/src/NuGet.Config
new file mode 100644
index 0000000000..dcccfdd5c1
--- /dev/null
+++ b/src/NuGet.Config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj
index 73983e7e30..86796e1dd7 100644
--- a/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj
+++ b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj
@@ -47,13 +47,15 @@
-
- True
- ..\packages\SqlServerCE.4.0.0.0\lib\System.Data.SqlServerCe.dll
+
+ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll
+ False
+ False
-
- True
- ..\packages\SqlServerCE.4.0.0.0\lib\System.Data.SqlServerCe.Entity.dll
+
+ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll
+ False
+ False
diff --git a/src/SQLCE4Umbraco/packages.config b/src/SQLCE4Umbraco/packages.config
index 5d2d5789e7..e2435f3e8b 100644
--- a/src/SQLCE4Umbraco/packages.config
+++ b/src/SQLCE4Umbraco/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs
index 6c766f8e97..caeb303f68 100644
--- a/src/SolutionInfo.cs
+++ b/src/SolutionInfo.cs
@@ -2,7 +2,7 @@
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
-[assembly: AssemblyCopyright("Copyright © Umbraco 2015")]
+[assembly: AssemblyCopyright("Copyright © Umbraco 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyFileVersion("7.3.0")]
-[assembly: AssemblyInformationalVersion("7.3.0")]
\ No newline at end of file
+[assembly: AssemblyFileVersion("7.5.0")]
+[assembly: AssemblyInformationalVersion("7.5.0")]
\ No newline at end of file
diff --git a/src/Umbraco.Core/ActionsResolver.cs b/src/Umbraco.Core/ActionsResolver.cs
index ff34f62c60..2da95a3416 100644
--- a/src/Umbraco.Core/ActionsResolver.cs
+++ b/src/Umbraco.Core/ActionsResolver.cs
@@ -11,7 +11,7 @@ namespace Umbraco.Core
///
/// A resolver to return all IAction objects
///
- internal sealed class ActionsResolver : LazyManyObjectsResolverBase
+ public sealed class ActionsResolver : LazyManyObjectsResolverBase
{
///
/// Constructor
diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs
index 4aef011d05..e47ef04650 100644
--- a/src/Umbraco.Core/ApplicationContext.cs
+++ b/src/Umbraco.Core/ApplicationContext.cs
@@ -283,7 +283,12 @@ namespace Umbraco.Core
{
var configStatus = ConfigurationStatus;
var currentVersion = UmbracoVersion.GetSemanticVersion();
- var ok = configStatus == currentVersion;
+
+ var ok =
+ //we are not configured if this is null
+ string.IsNullOrWhiteSpace(configStatus) == false
+ //they must match
+ && configStatus == currentVersion;
if (ok)
{
@@ -308,8 +313,9 @@ namespace Umbraco.Core
return ok;
}
- catch
+ catch (Exception ex)
{
+ LogHelper.Error("Error determining if application is configured, returning false", ex);
return false;
}
@@ -400,7 +406,8 @@ namespace Umbraco.Core
//clear the cache
if (ApplicationCache != null)
{
- ApplicationCache.ClearAllCache();
+ ApplicationCache.RuntimeCache.ClearAllCache();
+ ApplicationCache.IsolatedRuntimeCache.ClearAllCaches();
}
//reset all resolvers
ResolverCollection.ResetAll();
diff --git a/src/Umbraco.Core/AsyncLock.cs b/src/Umbraco.Core/AsyncLock.cs
index b4e8e64312..b6a3f8534e 100644
--- a/src/Umbraco.Core/AsyncLock.cs
+++ b/src/Umbraco.Core/AsyncLock.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -117,38 +118,55 @@ namespace Umbraco.Core
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
{
private readonly Semaphore _semaphore;
+ private GCHandle _handle;
internal NamedSemaphoreReleaser(Semaphore semaphore)
{
_semaphore = semaphore;
+ _handle = GCHandle.Alloc(_semaphore);
}
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
+ GC.SuppressFinalize(this); // finalize will not run
}
private void Dispose(bool disposing)
{
// critical
+ _handle.Free();
_semaphore.Release();
+ _semaphore.Dispose();
}
- // we WANT to release the semaphore because it's a system object
- // ie a critical non-managed resource - so we inherit from CriticalFinalizerObject
- // which means that the finalizer "should" run in all situations
+ // we WANT to release the semaphore because it's a system object, ie a critical
+ // non-managed resource - and if it is not released then noone else can acquire
+ // the lock - so we inherit from CriticalFinalizerObject which means that the
+ // finalizer "should" run in all situations - there is always a chance that it
+ // does not run and the semaphore remains "acquired" but then chances are the
+ // whole process (w3wp.exe...) is going down, at which point the semaphore will
+ // be destroyed by Windows.
- // however... that can fail with System.ObjectDisposedException because the
- // underlying handle was closed... because we cannot guarantee that the semaphore
- // is not gone already... unless we get a GCHandle = GCHandle.Alloc(_semaphore);
- // which should keep it around and then we free the handle?
+ // however, the semaphore is a managed object, and so when the finalizer runs it
+ // might have been finalized already, and then we get a, ObjectDisposedException
+ // in the finalizer - which is bad.
- // so... I'm not sure this is safe really...
+ // in order to prevent this we do two things
+ // - use a GCHandler to ensure the semaphore is still there when the finalizer
+ // runs, so we can actually release it
+ // - wrap the finalizer code in a try...catch to make sure it never throws
~NamedSemaphoreReleaser()
{
- Dispose(false);
+ try
+ {
+ Dispose(false);
+ }
+ catch
+ {
+ // we do NOT want the finalizer to throw - never ever
+ }
}
}
diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs
index 88d570beff..0c1a202b66 100644
--- a/src/Umbraco.Core/Cache/CacheKeys.cs
+++ b/src/Umbraco.Core/Cache/CacheKeys.cs
@@ -1,8 +1,9 @@
using System;
+using System.ComponentModel;
+using Umbraco.Core.CodeAnnotations;
namespace Umbraco.Core.Cache
{
-
///
/// Constants storing cache keys used in caching
///
@@ -12,52 +13,78 @@ namespace Umbraco.Core.Cache
public const string ApplicationsCacheKey = "ApplicationCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string UserTypeCacheKey = "UserTypeCache";
+ [Obsolete("This is no longer used and will be removed from the codebase in the future - it is referenced but no cache is stored against this key")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string ContentItemCacheKey = "contentItem";
+ [UmbracoWillObsolete("This cache key is only used for the legacy 'library' caching, remove in v8")]
public const string MediaCacheKey = "UL_GetMedia";
public const string MacroXsltCacheKey = "macroXslt_";
+
+ [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string MacroCacheKey = "UmbracoMacroCache";
+
public const string MacroHtmlCacheKey = "macroHtml_";
public const string MacroControlCacheKey = "macroControl_";
public const string MacroHtmlDateAddedCacheKey = "macroHtml_DateAdded_";
public const string MacroControlDateAddedCacheKey = "macroControl_DateAdded_";
+ [UmbracoWillObsolete("This cache key is only used for legacy 'library' member caching, remove in v8")]
public const string MemberLibraryCacheKey = "UL_GetMember";
+
+ [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string MemberBusinessLogicCacheKey = "MemberCacheItem_";
-
+
+ [UmbracoWillObsolete("This cache key is only used for legacy template business logic caching, remove in v8")]
public const string TemplateFrontEndCacheKey = "template";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache";
+ [Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string UserContextCacheKey = "UmbracoUserContext";
+
public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string UserCacheKey = "UmbracoUser";
public const string UserPermissionsCacheKey = "UmbracoUserPermissions";
+ [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string ContentTypeCacheKey = "UmbracoContentType";
+ [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
+ [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string LanguageCacheKey = "UmbracoLanguageCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string DomainCacheKey = "UmbracoDomainList";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string StylesheetCacheKey = "UmbracoStylesheet";
+
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
+ [Obsolete("This is no longer used and will be removed from the codebase in the future")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
diff --git a/src/Umbraco.Core/Cache/CacheRefresherBase.cs b/src/Umbraco.Core/Cache/CacheRefresherBase.cs
index 2931805b08..60ad69b6fc 100644
--- a/src/Umbraco.Core/Cache/CacheRefresherBase.cs
+++ b/src/Umbraco.Core/Cache/CacheRefresherBase.cs
@@ -2,6 +2,7 @@
using Umbraco.Core.Events;
using Umbraco.Core.Sync;
using umbraco.interfaces;
+using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
@@ -63,5 +64,15 @@ namespace Umbraco.Core.Cache
{
OnCacheUpdated(Instance, new CacheRefresherEventArgs(id, MessageType.RefreshById));
}
+
+ ///
+ /// Clears the cache for all repository entities of this type
+ ///
+ ///
+ internal void ClearAllIsolatedCacheByEntityType()
+ where TEntity : class, IAggregateRoot
+ {
+ ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.ClearCache();
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs
similarity index 93%
rename from src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs
rename to src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs
index 0b5b42660d..0ae721943d 100644
--- a/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs
+++ b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs
@@ -2,20 +2,27 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
-using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
-namespace Umbraco.Core.Persistence.Repositories
+namespace Umbraco.Core.Cache
{
+ ///
+ /// Interface describing this cache provider as a wrapper for another
+ ///
+ internal interface IRuntimeCacheProviderWrapper
+ {
+ IRuntimeCacheProvider InnerProvider { get; }
+ }
+
///
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
/// are a deep cloned copy of the item when the item is IDeepCloneable and that tracks changes are
/// reset if the object is TracksChangesEntityBase
///
- internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider
+ internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider, IRuntimeCacheProviderWrapper
{
- internal IRuntimeCacheProvider InnerProvider { get; private set; }
+ public IRuntimeCacheProvider InnerProvider { get; private set; }
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
{
diff --git a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs
new file mode 100644
index 0000000000..1f51fc3ccc
--- /dev/null
+++ b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs
@@ -0,0 +1,268 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// The default cache policy for retrieving a single entity
+ ///
+ ///
+ ///
+ ///
+ /// This cache policy uses sliding expiration and caches instances for 5 minutes. However if allow zero count is true, then we use the
+ /// default policy with no expiry.
+ ///
+ internal class DefaultRepositoryCachePolicy : RepositoryCachePolicyBase
+ where TEntity : class, IAggregateRoot
+ {
+ private readonly RepositoryCachePolicyOptions _options;
+
+ public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
+ : base(cache)
+ {
+ if (options == null) throw new ArgumentNullException("options");
+ _options = options;
+ }
+
+ protected string GetCacheIdKey(object id)
+ {
+ if (id == null) throw new ArgumentNullException("id");
+
+ return string.Format("{0}{1}", GetCacheTypeKey(), id);
+ }
+
+ protected string GetCacheTypeKey()
+ {
+ return string.Format("uRepo_{0}_", typeof(TEntity).Name);
+ }
+
+ public override void CreateOrUpdate(TEntity entity, Action persistMethod)
+ {
+ if (entity == null) throw new ArgumentNullException("entity");
+ if (persistMethod == null) throw new ArgumentNullException("persistMethod");
+
+ try
+ {
+ persistMethod(entity);
+
+ //set the disposal action
+ SetCacheAction(() =>
+ {
+ //just to be safe, we cannot cache an item without an identity
+ if (entity.HasIdentity)
+ {
+ Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity,
+ timeout: TimeSpan.FromMinutes(5),
+ isSliding: true);
+ }
+
+ //If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
+ Cache.ClearCacheItem(GetCacheTypeKey());
+ });
+
+ }
+ catch
+ {
+ //set the disposal action
+ SetCacheAction(() =>
+ {
+ //if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
+ // that we cache entities: http://issues.umbraco.org/issue/U4-4259
+ Cache.ClearCacheItem(GetCacheIdKey(entity.Id));
+
+ //If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
+ Cache.ClearCacheItem(GetCacheTypeKey());
+ });
+
+ throw;
+ }
+ }
+
+ public override void Remove(TEntity entity, Action persistMethod)
+ {
+ if (entity == null) throw new ArgumentNullException("entity");
+ if (persistMethod == null) throw new ArgumentNullException("persistMethod");
+
+ try
+ {
+ persistMethod(entity);
+ }
+ finally
+ {
+ //set the disposal action
+ var cacheKey = GetCacheIdKey(entity.Id);
+ SetCacheAction(() =>
+ {
+ Cache.ClearCacheItem(cacheKey);
+ //If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
+ Cache.ClearCacheItem(GetCacheTypeKey());
+ });
+ }
+ }
+
+ public override TEntity Get(TId id, Func getFromRepo)
+ {
+ if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
+
+ var cacheKey = GetCacheIdKey(id);
+ var fromCache = Cache.GetCacheItem(cacheKey);
+ if (fromCache != null)
+ return fromCache;
+
+ var entity = getFromRepo(id);
+
+ //set the disposal action
+ SetCacheAction(cacheKey, entity);
+
+ return entity;
+ }
+
+ public override TEntity Get(TId id)
+ {
+ var cacheKey = GetCacheIdKey(id);
+ return Cache.GetCacheItem(cacheKey);
+ }
+
+ public override bool Exists(TId id, Func getFromRepo)
+ {
+ if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
+
+ var cacheKey = GetCacheIdKey(id);
+ var fromCache = Cache.GetCacheItem(cacheKey);
+ return fromCache != null || getFromRepo(id);
+ }
+
+ public override TEntity[] GetAll(TId[] ids, Func> getFromRepo)
+ {
+ if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
+
+ if (ids.Any())
+ {
+ var entities = ids.Select(Get).ToArray();
+ if (ids.Length.Equals(entities.Length) && entities.Any(x => x == null) == false)
+ return entities;
+ }
+ else
+ {
+ var allEntities = GetAllFromCache();
+ if (allEntities.Any())
+ {
+ if (_options.GetAllCacheValidateCount)
+ {
+ //Get count of all entities of current type (TEntity) to ensure cached result is correct
+ var totalCount = _options.PerformCount();
+ if (allEntities.Length == totalCount)
+ return allEntities;
+ }
+ else
+ {
+ return allEntities;
+ }
+ }
+ else if (_options.GetAllCacheAllowZeroCount)
+ {
+ //if the repository allows caching a zero count, then check the zero count cache
+ if (HasZeroCountCache())
+ {
+ //there is a zero count cache so return an empty list
+ return new TEntity[] {};
+ }
+ }
+ }
+
+ //we need to do the lookup from the repo
+ var entityCollection = getFromRepo(ids)
+ //ensure we don't include any null refs in the returned collection!
+ .WhereNotNull()
+ .ToArray();
+
+ //set the disposal action
+ SetCacheAction(ids, entityCollection);
+
+ return entityCollection;
+ }
+
+ ///
+ /// Looks up the zero count cache, must return null if it doesn't exist
+ ///
+ ///
+ protected bool HasZeroCountCache()
+ {
+ var zeroCount = Cache.GetCacheItem(GetCacheTypeKey());
+ return (zeroCount != null && zeroCount.Any() == false);
+ }
+
+ ///
+ /// Performs the lookup for all entities of this type from the cache
+ ///
+ ///
+ protected TEntity[] GetAllFromCache()
+ {
+ var allEntities = Cache.GetCacheItemsByKeySearch(GetCacheTypeKey())
+ .WhereNotNull()
+ .ToArray();
+ return allEntities.Any() ? allEntities : new TEntity[] {};
+ }
+
+ ///
+ /// Sets the action to execute on disposal for a single entity
+ ///
+ ///
+ ///
+ protected virtual void SetCacheAction(string cacheKey, TEntity entity)
+ {
+ if (entity == null) return;
+
+ SetCacheAction(() =>
+ {
+ //just to be safe, we cannot cache an item without an identity
+ if (entity.HasIdentity)
+ {
+ Cache.InsertCacheItem(cacheKey, () => entity,
+ timeout: TimeSpan.FromMinutes(5),
+ isSliding: true);
+ }
+ });
+ }
+
+ ///
+ /// Sets the action to execute on disposal for an entity collection
+ ///
+ ///
+ ///
+ protected virtual void SetCacheAction(TId[] ids, TEntity[] entityCollection)
+ {
+ SetCacheAction(() =>
+ {
+ //This option cannot execute if we are looking up specific Ids
+ if (ids.Any() == false && entityCollection.Length == 0 && _options.GetAllCacheAllowZeroCount)
+ {
+ //there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache
+ // to signify that there is a zero count cache
+ //NOTE: Don't set expiry/sliding for a zero count
+ Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {});
+ }
+ else
+ {
+ //This is the default behavior, we'll individually cache each item so that if/when these items are resolved
+ // by id, they are returned from the already existing cache.
+ foreach (var entity in entityCollection.WhereNotNull())
+ {
+ var localCopy = entity;
+ //just to be safe, we cannot cache an item without an identity
+ if (localCopy.HasIdentity)
+ {
+ Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy,
+ timeout: TimeSpan.FromMinutes(5),
+ isSliding: true);
+ }
+ }
+ }
+ });
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicyFactory.cs
new file mode 100644
index 0000000000..5c02e41a48
--- /dev/null
+++ b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicyFactory.cs
@@ -0,0 +1,27 @@
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// Creates cache policies
+ ///
+ ///
+ ///
+ internal class DefaultRepositoryCachePolicyFactory : IRepositoryCachePolicyFactory
+ where TEntity : class, IAggregateRoot
+ {
+ private readonly IRuntimeCacheProvider _runtimeCache;
+ private readonly RepositoryCachePolicyOptions _options;
+
+ public DefaultRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
+ {
+ _runtimeCache = runtimeCache;
+ _options = options;
+ }
+
+ public virtual IRepositoryCachePolicy CreatePolicy()
+ {
+ return new DefaultRepositoryCachePolicy(_runtimeCache, _options);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs
new file mode 100644
index 0000000000..9b37d1861f
--- /dev/null
+++ b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs
@@ -0,0 +1,229 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Collections;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// A caching policy that caches an entire dataset as a single collection
+ ///
+ ///
+ ///
+ internal class FullDataSetRepositoryCachePolicy : RepositoryCachePolicyBase
+ where TEntity : class, IAggregateRoot
+ {
+ private readonly Func _getEntityId;
+ private readonly Func> _getAllFromRepo;
+ private readonly bool _expires;
+
+ public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func getEntityId, Func> getAllFromRepo, bool expires)
+ : base(cache)
+ {
+ _getEntityId = getEntityId;
+ _getAllFromRepo = getAllFromRepo;
+ _expires = expires;
+ }
+
+ private bool? _hasZeroCountCache;
+
+
+ protected string GetCacheTypeKey()
+ {
+ return string.Format("uRepo_{0}_", typeof(TEntity).Name);
+ }
+
+ public override void CreateOrUpdate(TEntity entity, Action persistMethod)
+ {
+ if (entity == null) throw new ArgumentNullException("entity");
+ if (persistMethod == null) throw new ArgumentNullException("persistMethod");
+
+ try
+ {
+ persistMethod(entity);
+
+ //set the disposal action
+ SetCacheAction(() =>
+ {
+ //Clear all
+ Cache.ClearCacheItem(GetCacheTypeKey());
+ });
+ }
+ catch
+ {
+ //set the disposal action
+ SetCacheAction(() =>
+ {
+ //Clear all
+ Cache.ClearCacheItem(GetCacheTypeKey());
+ });
+ throw;
+ }
+ }
+
+ public override void Remove(TEntity entity, Action persistMethod)
+ {
+ if (entity == null) throw new ArgumentNullException("entity");
+ if (persistMethod == null) throw new ArgumentNullException("persistMethod");
+
+ try
+ {
+ persistMethod(entity);
+ }
+ finally
+ {
+ //set the disposal action
+ SetCacheAction(() =>
+ {
+ //Clear all
+ Cache.ClearCacheItem(GetCacheTypeKey());
+ });
+ }
+ }
+
+ public override TEntity Get(TId id, Func getFromRepo)
+ {
+ //Force get all with cache
+ var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
+
+ //we don't have anything in cache (this should never happen), just return from the repo
+ if (found == null) return getFromRepo(id);
+ var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
+ if (entity == null) return null;
+
+ //We must ensure to deep clone each one out manually since the deep clone list only clones one way
+ return (TEntity)entity.DeepClone();
+ }
+
+ public override TEntity Get(TId id)
+ {
+ //Force get all with cache
+ var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
+
+ //we don't have anything in cache (this should never happen), just return null
+ if (found == null) return null;
+ var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
+ if (entity == null) return null;
+
+ //We must ensure to deep clone each one out manually since the deep clone list only clones one way
+ return (TEntity)entity.DeepClone();
+ }
+
+ public override bool Exists(TId id, Func getFromRepo)
+ {
+ //Force get all with cache
+ var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
+
+ //we don't have anything in cache (this should never happen), just return from the repo
+ return found == null
+ ? getFromRepo(id)
+ : found.Any(x => _getEntityId(x).Equals(id));
+ }
+
+ public override TEntity[] GetAll(TId[] ids, Func> getFromRepo)
+ {
+ //process getting all including setting the cache callback
+ var result = PerformGetAll(getFromRepo);
+
+ //now that the base result has been calculated, they will all be cached.
+ // Now we can just filter by ids if they have been supplied
+
+ return (ids.Any()
+ ? result.Where(x => ids.Contains(_getEntityId(x))).ToArray()
+ : result)
+ //We must ensure to deep clone each one out manually since the deep clone list only clones one way
+ .Select(x => (TEntity)x.DeepClone())
+ .ToArray();
+ }
+
+ private TEntity[] PerformGetAll(Func> getFromRepo)
+ {
+ var allEntities = GetAllFromCache();
+ if (allEntities.Any())
+ {
+ return allEntities;
+ }
+
+ //check the zero count cache
+ if (HasZeroCountCache())
+ {
+ //there is a zero count cache so return an empty list
+ return new TEntity[] { };
+ }
+
+ //we need to do the lookup from the repo
+ var entityCollection = getFromRepo(new TId[] { })
+ //ensure we don't include any null refs in the returned collection!
+ .WhereNotNull()
+ .ToArray();
+
+ //set the disposal action
+ SetCacheAction(entityCollection);
+
+ return entityCollection;
+ }
+
+ ///
+ /// For this type of caching policy, we don't cache individual items
+ ///
+ ///
+ ///
+ protected void SetCacheAction(string cacheKey, TEntity entity)
+ {
+ //No-op
+ }
+
+ ///
+ /// Sets the action to execute on disposal for an entity collection
+ ///
+ ///
+ protected void SetCacheAction(TEntity[] entityCollection)
+ {
+ //set the disposal action
+ SetCacheAction(() =>
+ {
+ //We want to cache the result as a single collection
+
+ if (_expires)
+ {
+ Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList(entityCollection),
+ timeout: TimeSpan.FromMinutes(5),
+ isSliding: true);
+ }
+ else
+ {
+ Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList(entityCollection));
+ }
+ });
+ }
+
+ ///
+ /// Looks up the zero count cache, must return null if it doesn't exist
+ ///
+ ///
+ protected bool HasZeroCountCache()
+ {
+ if (_hasZeroCountCache.HasValue)
+ return _hasZeroCountCache.Value;
+
+ _hasZeroCountCache = Cache.GetCacheItem>(GetCacheTypeKey()) != null;
+ return _hasZeroCountCache.Value;
+ }
+
+ ///
+ /// This policy will cache the full data set as a single collection
+ ///
+ ///
+ protected TEntity[] GetAllFromCache()
+ {
+ var found = Cache.GetCacheItem>(GetCacheTypeKey());
+
+ //This method will get called before checking for zero count cache, so we'll just set the flag here
+ _hasZeroCountCache = found != null;
+
+ return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs
new file mode 100644
index 0000000000..e4addcf355
--- /dev/null
+++ b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// Creates cache policies
+ ///
+ ///
+ ///
+ internal class FullDataSetRepositoryCachePolicyFactory : IRepositoryCachePolicyFactory
+ where TEntity : class, IAggregateRoot
+ {
+ private readonly IRuntimeCacheProvider _runtimeCache;
+ private readonly Func _getEntityId;
+ private readonly Func> _getAllFromRepo;
+ private readonly bool _expires;
+
+ public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, Func getEntityId, Func> getAllFromRepo, bool expires)
+ {
+ _runtimeCache = runtimeCache;
+ _getEntityId = getEntityId;
+ _getAllFromRepo = getAllFromRepo;
+ _expires = expires;
+ }
+
+ public virtual IRepositoryCachePolicy CreatePolicy()
+ {
+ return new FullDataSetRepositoryCachePolicy(_runtimeCache, _getEntityId, _getAllFromRepo, _expires);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/ICacheProvider.cs b/src/Umbraco.Core/Cache/ICacheProvider.cs
index 0d2bb1bdb6..e53123d7e8 100644
--- a/src/Umbraco.Core/Cache/ICacheProvider.cs
+++ b/src/Umbraco.Core/Cache/ICacheProvider.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Umbraco.Core.Cache
{
///
- /// An abstract class for implementing a basic cache provider
+ /// An interface for implementing a basic cache provider
///
public interface ICacheProvider
{
@@ -65,4 +65,4 @@ namespace Umbraco.Core.Cache
object GetCacheItem(string cacheKey, Func getCacheItem);
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs
new file mode 100644
index 0000000000..215487c3be
--- /dev/null
+++ b/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ internal interface IRepositoryCachePolicy : IDisposable
+ where TEntity : class, IAggregateRoot
+ {
+ TEntity Get(TId id, Func getFromRepo);
+ TEntity Get(TId id);
+ bool Exists(TId id, Func getFromRepo);
+
+ void CreateOrUpdate(TEntity entity, Action persistMethod);
+ void Remove(TEntity entity, Action persistMethod);
+ TEntity[] GetAll(TId[] ids, Func> getFromRepo);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/IRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/IRepositoryCachePolicyFactory.cs
new file mode 100644
index 0000000000..2d69704b63
--- /dev/null
+++ b/src/Umbraco.Core/Cache/IRepositoryCachePolicyFactory.cs
@@ -0,0 +1,9 @@
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ internal interface IRepositoryCachePolicyFactory where TEntity : class, IAggregateRoot
+ {
+ IRepositoryCachePolicy CreatePolicy();
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/IsolatedRuntimeCache.cs b/src/Umbraco.Core/Cache/IsolatedRuntimeCache.cs
new file mode 100644
index 0000000000..da20f7eb73
--- /dev/null
+++ b/src/Umbraco.Core/Cache/IsolatedRuntimeCache.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Concurrent;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// Used to get/create/manipulate isolated runtime cache
+ ///
+ ///
+ /// This is useful for repository level caches to ensure that cache lookups by key are fast so
+ /// that the repository doesn't need to search through all keys on a global scale.
+ ///
+ public class IsolatedRuntimeCache
+ {
+ internal Func CacheFactory { get; set; }
+
+ ///
+ /// Constructor that allows specifying a factory for the type of runtime isolated cache to create
+ ///
+ ///
+ public IsolatedRuntimeCache(Func cacheFactory)
+ {
+ CacheFactory = cacheFactory;
+ }
+
+ private readonly ConcurrentDictionary _isolatedCache = new ConcurrentDictionary();
+
+ ///
+ /// Returns an isolated runtime cache for a given type
+ ///
+ ///
+ ///
+ public IRuntimeCacheProvider GetOrCreateCache()
+ {
+ return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
+ }
+
+ ///
+ /// Returns an isolated runtime cache for a given type
+ ///
+ ///
+ public IRuntimeCacheProvider GetOrCreateCache(Type type)
+ {
+ return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
+ }
+
+ ///
+ /// Tries to get a cache by the type specified
+ ///
+ ///
+ ///
+ public Attempt GetCache()
+ {
+ IRuntimeCacheProvider cache;
+ if (_isolatedCache.TryGetValue(typeof(T), out cache))
+ {
+ return Attempt.Succeed(cache);
+ }
+ return Attempt.Fail();
+ }
+
+ ///
+ /// Clears all values inside this isolated runtime cache
+ ///
+ ///
+ ///
+ public void ClearCache()
+ {
+ IRuntimeCacheProvider cache;
+ if (_isolatedCache.TryGetValue(typeof(T), out cache))
+ {
+ cache.ClearAllCache();
+ }
+ }
+
+ ///
+ /// Clears all of the isolated caches
+ ///
+ public void ClearAllCaches()
+ {
+ foreach (var key in _isolatedCache.Keys)
+ {
+ IRuntimeCacheProvider cache;
+ if (_isolatedCache.TryRemove(key, out cache))
+ {
+ cache.ClearAllCache();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/OnlySingleItemsRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/OnlySingleItemsRepositoryCachePolicyFactory.cs
new file mode 100644
index 0000000000..b24838bc3b
--- /dev/null
+++ b/src/Umbraco.Core/Cache/OnlySingleItemsRepositoryCachePolicyFactory.cs
@@ -0,0 +1,27 @@
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// Creates cache policies
+ ///
+ ///
+ ///
+ internal class OnlySingleItemsRepositoryCachePolicyFactory : IRepositoryCachePolicyFactory
+ where TEntity : class, IAggregateRoot
+ {
+ private readonly IRuntimeCacheProvider _runtimeCache;
+ private readonly RepositoryCachePolicyOptions _options;
+
+ public OnlySingleItemsRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
+ {
+ _runtimeCache = runtimeCache;
+ _options = options;
+ }
+
+ public virtual IRepositoryCachePolicy CreatePolicy()
+ {
+ return new SingleItemsOnlyRepositoryCachePolicy(_runtimeCache, _options);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs b/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs
new file mode 100644
index 0000000000..b939cd14e6
--- /dev/null
+++ b/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ internal abstract class RepositoryCachePolicyBase : DisposableObject, IRepositoryCachePolicy
+ where TEntity : class, IAggregateRoot
+ {
+ private Action _action;
+
+ protected RepositoryCachePolicyBase(IRuntimeCacheProvider cache)
+ {
+ if (cache == null) throw new ArgumentNullException("cache");
+
+ Cache = cache;
+ }
+
+ protected IRuntimeCacheProvider Cache { get; private set; }
+
+ ///
+ /// The disposal performs the caching
+ ///
+ protected override void DisposeResources()
+ {
+ if (_action != null)
+ {
+ _action();
+ }
+ }
+
+ ///
+ /// Sets the action to execute on disposal
+ ///
+ ///
+ protected void SetCacheAction(Action action)
+ {
+ _action = action;
+ }
+
+ public abstract TEntity Get(TId id, Func getFromRepo);
+ public abstract TEntity Get(TId id);
+ public abstract bool Exists(TId id, Func getFromRepo);
+ public abstract void CreateOrUpdate(TEntity entity, Action persistMethod);
+ public abstract void Remove(TEntity entity, Action persistMethod);
+ public abstract TEntity[] GetAll(TId[] ids, Func> getFromRepo);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheOptions.cs b/src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs
similarity index 57%
rename from src/Umbraco.Core/Persistence/Repositories/RepositoryCacheOptions.cs
rename to src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs
index 9ac8aa6abd..e8c6ac02b0 100644
--- a/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheOptions.cs
+++ b/src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs
@@ -1,17 +1,34 @@
-namespace Umbraco.Core.Persistence.Repositories
+using System;
+
+namespace Umbraco.Core.Cache
{
- internal class RepositoryCacheOptions
+ internal class RepositoryCachePolicyOptions
{
///
- /// Constructor sets defaults
+ /// Ctor - sets GetAllCacheValidateCount = true
///
- public RepositoryCacheOptions()
+ public RepositoryCachePolicyOptions(Func performCount)
{
+ PerformCount = performCount;
GetAllCacheValidateCount = true;
GetAllCacheAllowZeroCount = false;
- GetAllCacheThresholdLimit = 100;
}
+ ///
+ /// Ctor - sets GetAllCacheValidateCount = false
+ ///
+ public RepositoryCachePolicyOptions()
+ {
+ PerformCount = null;
+ GetAllCacheValidateCount = false;
+ GetAllCacheAllowZeroCount = false;
+ }
+
+ ///
+ /// Callback required to get count for GetAllCacheValidateCount
+ ///
+ public Func PerformCount { get; private set; }
+
///
/// True/false as to validate the total item count when all items are returned from cache, the default is true but this
/// means that a db lookup will occur - though that lookup will probably be significantly less expensive than the normal
@@ -21,16 +38,11 @@ namespace Umbraco.Core.Persistence.Repositories
/// setting this to return false will improve performance of GetAll cache with no params but should only be used
/// for specific circumstances
///
- public bool GetAllCacheValidateCount { get; set; }
+ public bool GetAllCacheValidateCount { get; private set; }
///
/// True if the GetAll method will cache that there are zero results so that the db is not hit when there are no results found
///
public bool GetAllCacheAllowZeroCount { get; set; }
-
- ///
- /// The threshold entity count for which the GetAll method will cache entities
- ///
- public int GetAllCacheThresholdLimit { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/SingleItemsOnlyRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/SingleItemsOnlyRepositoryCachePolicy.cs
new file mode 100644
index 0000000000..28ac4ee2d1
--- /dev/null
+++ b/src/Umbraco.Core/Cache/SingleItemsOnlyRepositoryCachePolicy.cs
@@ -0,0 +1,24 @@
+using System.Linq;
+using Umbraco.Core.Collections;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// A caching policy that ignores all caches for GetAll - it will only cache calls for individual items
+ ///
+ ///
+ ///
+ internal class SingleItemsOnlyRepositoryCachePolicy : DefaultRepositoryCachePolicy
+ where TEntity : class, IAggregateRoot
+ {
+ public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) : base(cache, options)
+ {
+ }
+
+ protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
+ {
+ //no-op
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs
index 51cf37aa23..0dc5f5b00f 100644
--- a/src/Umbraco.Core/CacheHelper.cs
+++ b/src/Umbraco.Core/CacheHelper.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections;
+using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
@@ -11,20 +13,15 @@ using Umbraco.Core.Logging;
namespace Umbraco.Core
{
-
///
/// Class that is exposed by the ApplicationContext for application wide caching purposes
///
public class CacheHelper
{
- private readonly bool _enableCache;
- private readonly ICacheProvider _requestCache;
- private readonly ICacheProvider _nullRequestCache = new NullCacheProvider();
- private readonly ICacheProvider _staticCache;
- private readonly ICacheProvider _nullStaticCache = new NullCacheProvider();
- private readonly IRuntimeCacheProvider _httpCache;
- private readonly IRuntimeCacheProvider _nullHttpCache = new NullCacheProvider();
-
+ private static readonly ICacheProvider NullRequestCache = new NullCacheProvider();
+ private static readonly ICacheProvider NullStaticCache = new NullCacheProvider();
+ private static readonly IRuntimeCacheProvider NullRuntimeCache = new NullCacheProvider();
+
///
/// Creates a cache helper with disabled caches
///
@@ -34,7 +31,7 @@ namespace Umbraco.Core
///
public static CacheHelper CreateDisabledCacheHelper()
{
- return new CacheHelper(null, null, null, false);
+ return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, new IsolatedRuntimeCache(t => NullRuntimeCache));
}
///
@@ -44,7 +41,8 @@ namespace Umbraco.Core
: this(
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
new StaticCacheProvider(),
- new HttpRequestCacheProvider())
+ new HttpRequestCacheProvider(),
+ new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
{
}
@@ -56,93 +54,75 @@ namespace Umbraco.Core
: this(
new HttpRuntimeCacheProvider(cache),
new StaticCacheProvider(),
- new HttpRequestCacheProvider())
+ new HttpRequestCacheProvider(),
+ new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
{
}
- ///
- /// Initializes a new instance based on the provided providers
- ///
- ///
- ///
- ///
+ [Obsolete("Use the constructor the specifies all dependencies")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public CacheHelper(
IRuntimeCacheProvider httpCacheProvider,
ICacheProvider staticCacheProvider,
ICacheProvider requestCacheProvider)
- : this(httpCacheProvider, staticCacheProvider, requestCacheProvider, true)
+ : this(httpCacheProvider, staticCacheProvider, requestCacheProvider, new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
{
}
- ///
- /// Private ctor used for creating a disabled cache helper
- ///
- ///
- ///
- ///
- ///
- private CacheHelper(
+ ///
+ /// Initializes a new instance based on the provided providers
+ ///
+ ///
+ ///
+ ///
+ ///
+ public CacheHelper(
IRuntimeCacheProvider httpCacheProvider,
ICacheProvider staticCacheProvider,
- ICacheProvider requestCacheProvider,
- bool enableCache)
+ ICacheProvider requestCacheProvider,
+ IsolatedRuntimeCache isolatedCacheManager)
{
- if (enableCache)
- {
- _httpCache = httpCacheProvider;
- _staticCache = staticCacheProvider;
- _requestCache = requestCacheProvider;
- }
- else
- {
- _httpCache = null;
- _staticCache = null;
- _requestCache = null;
- }
-
- _enableCache = enableCache;
+ if (httpCacheProvider == null) throw new ArgumentNullException("httpCacheProvider");
+ if (staticCacheProvider == null) throw new ArgumentNullException("staticCacheProvider");
+ if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider");
+ if (isolatedCacheManager == null) throw new ArgumentNullException("isolatedCacheManager");
+ RuntimeCache = httpCacheProvider;
+ StaticCache = staticCacheProvider;
+ RequestCache = requestCacheProvider;
+ IsolatedRuntimeCache = isolatedCacheManager;
}
///
/// Returns the current Request cache
///
- public ICacheProvider RequestCache
- {
- get { return _enableCache ? _requestCache : _nullRequestCache; }
- }
-
- ///
- /// Returns the current Runtime cache
- ///
- public ICacheProvider StaticCache
- {
- get { return _enableCache ? _staticCache : _nullStaticCache; }
- }
-
- ///
- /// Returns the current Runtime cache
- ///
- public IRuntimeCacheProvider RuntimeCache
- {
- get { return _enableCache ? _httpCache : _nullHttpCache; }
- }
+ public ICacheProvider RequestCache { get; internal set; }
- #region Legacy Runtime/Http Cache accessors
+ ///
+ /// Returns the current Runtime cache
+ ///
+ public ICacheProvider StaticCache { get; internal set; }
+
+ ///
+ /// Returns the current Runtime cache
+ ///
+ public IRuntimeCacheProvider RuntimeCache { get; internal set; }
+
+ ///
+ /// Returns the current Isolated Runtime cache manager
+ ///
+ public IsolatedRuntimeCache IsolatedRuntimeCache { get; internal set; }
+
+ #region Legacy Runtime/Http Cache accessors
///
/// Clears the item in umbraco's runtime cache
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public void ClearAllCache()
{
- if (_enableCache == false)
- {
- _nullHttpCache.ClearAllCache();
- }
- else
- {
- _httpCache.ClearAllCache();
- }
+ RuntimeCache.ClearAllCache();
+ IsolatedRuntimeCache.ClearAllCaches();
}
///
@@ -150,16 +130,10 @@ namespace Umbraco.Core
///
/// Key
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public void ClearCacheItem(string key)
{
- if (_enableCache == false)
- {
- _nullHttpCache.ClearCacheItem(key);
- }
- else
- {
- _httpCache.ClearCacheItem(key);
- }
+ RuntimeCache.ClearCacheItem(key);
}
@@ -171,30 +145,17 @@ namespace Umbraco.Core
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
public void ClearCacheObjectTypes(string typeName)
{
- if (_enableCache == false)
- {
- _nullHttpCache.ClearCacheObjectTypes(typeName);
- }
- else
- {
- _httpCache.ClearCacheObjectTypes(typeName);
- }
+ RuntimeCache.ClearCacheObjectTypes(typeName);
}
///
/// Clears all objects in the System.Web.Cache with the System.Type specified
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public void ClearCacheObjectTypes()
{
- if (_enableCache == false)
- {
- _nullHttpCache.ClearCacheObjectTypes();
- }
- else
- {
- _httpCache.ClearCacheObjectTypes();
- }
+ RuntimeCache.ClearCacheObjectTypes();
}
///
@@ -202,16 +163,10 @@ namespace Umbraco.Core
///
/// The start of the key
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public void ClearCacheByKeySearch(string keyStartsWith)
{
- if (_enableCache == false)
- {
- _nullHttpCache.ClearCacheByKeySearch(keyStartsWith);
- }
- else
- {
- _httpCache.ClearCacheByKeySearch(keyStartsWith);
- }
+ RuntimeCache.ClearCacheByKeySearch(keyStartsWith);
}
///
@@ -219,29 +174,17 @@ namespace Umbraco.Core
///
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public void ClearCacheByKeyExpression(string regexString)
{
- if (_enableCache == false)
- {
- _nullHttpCache.ClearCacheByKeyExpression(regexString);
- }
- else
- {
- _httpCache.ClearCacheByKeyExpression(regexString);
- }
+ RuntimeCache.ClearCacheByKeyExpression(regexString);
}
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable GetCacheItemsByKeySearch(string keyStartsWith)
{
- if (_enableCache == false)
- {
- return _nullHttpCache.GetCacheItemsByKeySearch(keyStartsWith);
- }
- else
- {
- return _httpCache.GetCacheItemsByKeySearch(keyStartsWith);
- }
+ return RuntimeCache.GetCacheItemsByKeySearch(keyStartsWith);
}
///
@@ -251,16 +194,10 @@ namespace Umbraco.Core
///
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public TT GetCacheItem(string cacheKey)
{
- if (_enableCache == false)
- {
- return _nullHttpCache.GetCacheItem(cacheKey);
- }
- else
- {
- return _httpCache.GetCacheItem(cacheKey);
- }
+ return RuntimeCache.GetCacheItem(cacheKey);
}
///
@@ -271,16 +208,11 @@ namespace Umbraco.Core
///
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public TT GetCacheItem(string cacheKey, Func getCacheItem)
{
- if (_enableCache == false)
- {
- return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem);
- }
- else
- {
- return _httpCache.GetCacheItem(cacheKey, getCacheItem);
- }
+ return RuntimeCache.GetCacheItem(cacheKey, getCacheItem);
+
}
///
@@ -292,17 +224,12 @@ namespace Umbraco.Core
///
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public TT GetCacheItem(string cacheKey,
TimeSpan timeout, Func getCacheItem)
{
- if (_enableCache == false)
- {
- return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout);
- }
- else
- {
- return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout);
- }
+ return RuntimeCache.GetCacheItem(cacheKey, getCacheItem, timeout);
+
}
///
@@ -315,18 +242,13 @@ namespace Umbraco.Core
///
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public TT GetCacheItem(string cacheKey,
CacheItemRemovedCallback refreshAction, TimeSpan timeout,
Func getCacheItem)
{
- if (!_enableCache)
- {
- return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
- }
- else
- {
- return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
- }
+ return RuntimeCache.GetCacheItem(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
+
}
///
@@ -340,18 +262,13 @@ namespace Umbraco.Core
///
///
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public TT GetCacheItem(string cacheKey,
CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout,
Func getCacheItem)
{
- if (_enableCache == false)
- {
- return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
- }
- else
- {
- return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
- }
+ return RuntimeCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
+
}
///
@@ -373,20 +290,13 @@ namespace Umbraco.Core
TimeSpan timeout,
Func getCacheItem)
{
- if (_enableCache == false)
+ var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
+ if (cache != null)
{
- return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
- }
- else
- {
- var cache = _httpCache as HttpRuntimeCacheProvider;
- if (cache != null)
- {
- var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
- return result == null ? default(TT) : result.TryConvertTo().Result;
- }
- throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
+ var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
+ return result == null ? default(TT) : result.TryConvertTo().Result;
}
+ throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
}
///
@@ -404,20 +314,13 @@ namespace Umbraco.Core
CacheDependency cacheDependency,
Func getCacheItem)
{
- if (!_enableCache)
+ var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
+ if (cache != null)
{
- return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, null, false, priority, null, null);
- }
- else
- {
- var cache = _httpCache as HttpRuntimeCacheProvider;
- if (cache != null)
- {
- var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
- return result == null ? default(TT) : result.TryConvertTo().Result;
- }
- throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
+ var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
+ return result == null ? default(TT) : result.TryConvertTo().Result;
}
+ throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
}
///
@@ -427,18 +330,14 @@ namespace Umbraco.Core
///
///
///
+ [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public void InsertCacheItem(string cacheKey,
CacheItemPriority priority,
Func getCacheItem)
{
- if (_enableCache == false)
- {
- _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, priority: priority);
- }
- else
- {
- _httpCache.InsertCacheItem(cacheKey, getCacheItem, priority: priority);
- }
+ RuntimeCache.InsertCacheItem(cacheKey, getCacheItem, priority: priority);
+
}
///
@@ -449,19 +348,14 @@ namespace Umbraco.Core
///
/// This will set an absolute expiration from now until the timeout
///
+ [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public void InsertCacheItem(string cacheKey,
CacheItemPriority priority,
TimeSpan timeout,
Func getCacheItem)
{
- if (_enableCache == false)
- {
- _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority);
- }
- else
- {
- _httpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority);
- }
+ RuntimeCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority);
}
///
@@ -480,19 +374,12 @@ namespace Umbraco.Core
TimeSpan timeout,
Func getCacheItem)
{
- if (_enableCache == false)
+ var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
+ if (cache != null)
{
- _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority, dependentFiles:null);
- }
- else
- {
- var cache = _httpCache as HttpRuntimeCacheProvider;
- if (cache != null)
- {
- cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
- }
- throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
+ cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
}
+ throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
}
///
@@ -513,22 +400,29 @@ namespace Umbraco.Core
TimeSpan? timeout,
Func getCacheItem)
{
- if (_enableCache == false)
+ var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
+ if (cache != null)
{
- _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
- }
- else
- {
- var cache = _httpCache as HttpRuntimeCacheProvider;
- if (cache != null)
- {
- cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
- }
- throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
+ cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
}
+ throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
}
#endregion
- }
+ private HttpRuntimeCacheProvider GetHttpRuntimeCacheProvider(IRuntimeCacheProvider runtimeCache)
+ {
+ HttpRuntimeCacheProvider cache;
+ var wrapper = RuntimeCache as IRuntimeCacheProviderWrapper;
+ if (wrapper != null)
+ {
+ cache = wrapper.InnerProvider as HttpRuntimeCacheProvider;
+ }
+ else
+ {
+ cache = RuntimeCache as HttpRuntimeCacheProvider;
+ }
+ return cache;
+ }
+ }
}
diff --git a/src/Umbraco.Core/Collections/DeepCloneableList.cs b/src/Umbraco.Core/Collections/DeepCloneableList.cs
new file mode 100644
index 0000000000..5067562aa7
--- /dev/null
+++ b/src/Umbraco.Core/Collections/DeepCloneableList.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Collections
+{
+ ///
+ /// A List that can be deep cloned with deep cloned elements and can reset the collection's items dirty flags
+ ///
+ ///
+ internal class DeepCloneableList : List, IDeepCloneable, IRememberBeingDirty
+ {
+ private readonly ListCloneBehavior _listCloneBehavior;
+
+ public DeepCloneableList(ListCloneBehavior listCloneBehavior)
+ {
+ _listCloneBehavior = listCloneBehavior;
+ }
+
+ public DeepCloneableList(IEnumerable collection, ListCloneBehavior listCloneBehavior) : base(collection)
+ {
+ _listCloneBehavior = listCloneBehavior;
+ }
+
+ ///
+ /// Default behavior is CloneOnce
+ ///
+ ///
+ public DeepCloneableList(IEnumerable collection)
+ : this(collection, ListCloneBehavior.CloneOnce)
+ {
+ }
+
+ ///
+ /// Creates a new list and adds each element as a deep cloned element if it is of type IDeepCloneable
+ ///
+ ///
+ public object DeepClone()
+ {
+ switch (_listCloneBehavior)
+ {
+ case ListCloneBehavior.CloneOnce:
+ //we are cloning once, so create a new list in none mode
+ // and deep clone all items into it
+ var newList = new DeepCloneableList(ListCloneBehavior.None);
+ foreach (var item in this)
+ {
+ var dc = item as IDeepCloneable;
+ if (dc != null)
+ {
+ newList.Add((T)dc.DeepClone());
+ }
+ else
+ {
+ newList.Add(item);
+ }
+ }
+ return newList;
+ case ListCloneBehavior.None:
+ //we are in none mode, so just return a new list with the same items
+ return new DeepCloneableList(this, ListCloneBehavior.None);
+ case ListCloneBehavior.Always:
+ //always clone to new list
+ var newList2 = new DeepCloneableList(ListCloneBehavior.Always);
+ foreach (var item in this)
+ {
+ var dc = item as IDeepCloneable;
+ if (dc != null)
+ {
+ newList2.Add((T)dc.DeepClone());
+ }
+ else
+ {
+ newList2.Add(item);
+ }
+ }
+ return newList2;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public bool IsDirty()
+ {
+ return this.OfType().Any(x => x.IsDirty());
+ }
+
+ public bool WasDirty()
+ {
+ return this.OfType().Any(x => x.WasDirty());
+ }
+
+ ///
+ /// Always returns false, the list has no properties we need to report
+ ///
+ ///
+ ///
+ public bool IsPropertyDirty(string propName)
+ {
+ return false;
+ }
+
+ ///
+ /// Always returns false, the list has no properties we need to report
+ ///
+ ///
+ ///
+ public bool WasPropertyDirty(string propertyName)
+ {
+ return false;
+ }
+
+ public void ResetDirtyProperties()
+ {
+ foreach (var dc in this.OfType())
+ {
+ dc.ResetDirtyProperties();
+ }
+ }
+
+ public void ForgetPreviouslyDirtyProperties()
+ {
+ foreach (var dc in this.OfType())
+ {
+ dc.ForgetPreviouslyDirtyProperties();
+ }
+ }
+
+ public void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
+ {
+ foreach (var dc in this.OfType())
+ {
+ dc.ResetDirtyProperties(rememberPreviouslyChangedProperties);
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Collections/ListCloneBehavior.cs b/src/Umbraco.Core/Collections/ListCloneBehavior.cs
new file mode 100644
index 0000000000..4fe935f7ff
--- /dev/null
+++ b/src/Umbraco.Core/Collections/ListCloneBehavior.cs
@@ -0,0 +1,20 @@
+namespace Umbraco.Core.Collections
+{
+ internal enum ListCloneBehavior
+ {
+ ///
+ /// When set, DeepClone will clone the items one time and the result list behavior will be None
+ ///
+ CloneOnce,
+
+ ///
+ /// When set, DeepClone will not clone any items
+ ///
+ None,
+
+ ///
+ /// When set, DeepClone will always clone all items
+ ///
+ Always
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs
index 2c3855727b..525bff2999 100644
--- a/src/Umbraco.Core/Configuration/GlobalSettings.cs
+++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Configuration
//make this volatile so that we can ensure thread safety with a double check lock
private static volatile string _reservedUrlsCache;
private static string _reservedPathsCache;
- private static StartsWithContainer _reservedList = new StartsWithContainer();
+ private static HashSet _reservedList = new HashSet();
private static string _reservedPaths;
private static string _reservedUrls;
//ensure the built on (non-changeable) reserved paths are there at all times
@@ -767,38 +767,31 @@ namespace Umbraco.Core.Configuration
// store references to strings to determine changes
_reservedPathsCache = GlobalSettings.ReservedPaths;
_reservedUrlsCache = GlobalSettings.ReservedUrls;
-
- string _root = SystemDirectories.Root.Trim().ToLower();
-
+
// add URLs and paths to a new list
- StartsWithContainer _newReservedList = new StartsWithContainer();
- foreach (string reservedUrl in _reservedUrlsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
+ var newReservedList = new HashSet();
+ foreach (var reservedUrlTrimmed in _reservedUrlsCache
+ .Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim().ToLowerInvariant())
+ .Where(x => x.IsNullOrWhiteSpace() == false)
+ .Select(reservedUrl => IOHelper.ResolveUrl(reservedUrl).Trim().EnsureStartsWith("/"))
+ .Where(reservedUrlTrimmed => reservedUrlTrimmed.IsNullOrWhiteSpace() == false))
{
- if (string.IsNullOrWhiteSpace(reservedUrl))
- continue;
-
-
- //resolves the url to support tilde chars
- string reservedUrlTrimmed = IOHelper.ResolveUrl(reservedUrl.Trim()).Trim().ToLower();
- if (reservedUrlTrimmed.Length > 0)
- _newReservedList.Add(reservedUrlTrimmed);
+ newReservedList.Add(reservedUrlTrimmed);
}
- foreach (string reservedPath in _reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
+ foreach (var reservedPathTrimmed in _reservedPathsCache
+ .Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim().ToLowerInvariant())
+ .Where(x => x.IsNullOrWhiteSpace() == false)
+ .Select(reservedPath => IOHelper.ResolveUrl(reservedPath).Trim().EnsureStartsWith("/").EnsureEndsWith("/"))
+ .Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false))
{
- bool trimEnd = !reservedPath.EndsWith("/");
- if (string.IsNullOrWhiteSpace(reservedPath))
- continue;
-
- //resolves the url to support tilde chars
- string reservedPathTrimmed = IOHelper.ResolveUrl(reservedPath.Trim()).Trim().ToLower();
-
- if (reservedPathTrimmed.Length > 0)
- _newReservedList.Add(reservedPathTrimmed + (reservedPathTrimmed.EndsWith("/") ? "" : "/"));
+ newReservedList.Add(reservedPathTrimmed);
}
// use the new list from now on
- _reservedList = _newReservedList;
+ _reservedList = newReservedList;
}
}
}
@@ -806,107 +799,17 @@ namespace Umbraco.Core.Configuration
//The url should be cleaned up before checking:
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
// * We shouldn't be comparing the query at all
- var pathPart = url.Split('?')[0];
- if (!pathPart.Contains(".") && !pathPart.EndsWith("/"))
+ var pathPart = url.Split(new[] {'?'}, StringSplitOptions.RemoveEmptyEntries)[0].ToLowerInvariant();
+ if (pathPart.Contains(".") == false)
{
- pathPart += "/";
+ pathPart = pathPart.EnsureEndsWith('/');
}
// return true if url starts with an element of the reserved list
- return _reservedList.StartsWith(pathPart.ToLowerInvariant());
+ return _reservedList.Any(x => pathPart.InvariantStartsWith(x));
}
- ///
- /// Structure that checks in logarithmic time
- /// if a given string starts with one of the added keys.
- ///
- private class StartsWithContainer
- {
- /// Internal sorted list of keys.
- public SortedList _list
- = new SortedList(StartsWithComparator.Instance);
-
- ///
- /// Adds the specified new key.
- ///
- /// The new key.
- public void Add(string newKey)
- {
- // if the list already contains an element that begins with newKey, return
- if (String.IsNullOrEmpty(newKey) || StartsWith(newKey))
- return;
-
- // create a new collection, so the old one can still be accessed
- SortedList newList
- = new SortedList(_list.Count + 1, StartsWithComparator.Instance);
-
- // add only keys that don't already start with newKey, others are unnecessary
- foreach (string key in _list.Keys)
- if (!key.StartsWith(newKey))
- newList.Add(key, null);
- // add the new key
- newList.Add(newKey, null);
-
- // update the list (thread safe, _list was never in incomplete state)
- _list = newList;
- }
-
- ///
- /// Checks if the given string starts with any of the added keys.
- ///
- /// The target.
- /// true if a key is found that matches the start of target
- ///
- /// Runs in O(s*log(n)), with n the number of keys and s the length of target.
- ///
- public bool StartsWith(string target)
- {
- return _list.ContainsKey(target);
- }
-
- /// Comparator that tests if a string starts with another.
- /// Not a real comparator, since it is not reflexive. (x==y does not imply y==x)
- private sealed class StartsWithComparator : IComparer
- {
- /// Default string comparer.
- private readonly static Comparer _stringComparer = Comparer.Default;
-
- /// Gets an instance of the StartsWithComparator.
- public static readonly StartsWithComparator Instance = new StartsWithComparator();
-
- ///
- /// Tests if whole begins with all characters of part.
- ///
- /// The part.
- /// The whole.
- ///
- /// Returns 0 if whole starts with part, otherwise performs standard string comparison.
- ///
- public int Compare(string part, string whole)
- {
- // let the default string comparer deal with null or when part is not smaller then whole
- if (part == null || whole == null || part.Length >= whole.Length)
- return _stringComparer.Compare(part, whole);
-
- ////ensure both have a / on the end
- //part = part.EndsWith("/") ? part : part + "/";
- //whole = whole.EndsWith("/") ? whole : whole + "/";
- //if (part.Length >= whole.Length)
- // return _stringComparer.Compare(part, whole);
-
- // loop through all characters that part and whole have in common
- int pos = 0;
- bool match;
- do
- {
- match = (part[pos] == whole[pos]);
- } while (match && ++pos < part.Length);
-
- // return result of last comparison
- return match ? 0 : (part[pos] < whole[pos] ? -1 : 1);
- }
- }
- }
+
}
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
index ced63f04bb..1b1404a897 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
@@ -280,6 +280,18 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
}
}
+ [ConfigurationProperty("EnableInheritedDocumentTypes")]
+ internal InnerTextConfigurationElement EnableInheritedDocumentTypes
+ {
+ get
+ {
+ return new OptionalInnerTextConfigurationElement(
+ (InnerTextConfigurationElement) this["EnableInheritedDocumentTypes"],
+ //set the default
+ true);
+ }
+ }
+
string IContentSection.NotificationEmailAddress
{
get { return Notifications.NotificationEmailAddress; }
@@ -414,5 +426,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
get { return DefaultDocumentTypeProperty; }
}
+
+ bool IContentSection.EnableInheritedDocumentTypes
+ {
+ get { return EnableInheritedDocumentTypes; }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
index 93e3260b44..ebdd9ae637 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
@@ -59,5 +59,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
bool GlobalPreviewStorageEnabled { get; }
string DefaultDocumentTypeProperty { get; }
+
+ bool EnableInheritedDocumentTypes { get; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs
index c3a1df301d..c44c0cf0df 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs
@@ -6,6 +6,8 @@
bool HideDisabledUsersInBackoffice { get; }
+ bool AllowPasswordReset { get; }
+
string AuthCookieName { get; }
string AuthCookieDomain { get; }
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs
index 34642c8c90..f280b3e20c 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs
@@ -28,6 +28,18 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
}
}
+ [ConfigurationProperty("allowPasswordReset")]
+ internal InnerTextConfigurationElement AllowPasswordReset
+ {
+ get
+ {
+ return new OptionalInnerTextConfigurationElement(
+ (InnerTextConfigurationElement)this["allowPasswordReset"],
+ //set the default
+ true);
+ }
+ }
+
[ConfigurationProperty("authCookieName")]
internal InnerTextConfigurationElement AuthCookieName
{
@@ -62,6 +74,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return HideDisabledUsersInBackoffice; }
}
+ bool ISecuritySection.AllowPasswordReset
+ {
+ get { return AllowPasswordReset; }
+ }
+
string ISecuritySection.AuthCookieName
{
get { return AuthCookieName; }
diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
index e61d091d84..762d3da59c 100644
--- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
- private static readonly Version Version = new Version("7.3.0");
+ private static readonly Version Version = new Version("7.5.0");
///
/// Gets the current version of Umbraco.
diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs
index f03108f4ac..12f7076fc4 100644
--- a/src/Umbraco.Core/Constants-Applications.cs
+++ b/src/Umbraco.Core/Constants-Applications.cs
@@ -43,7 +43,7 @@
public const string Users = "users";
///
- /// Application alias for the users section.
+ /// Application alias for the forms section.
///
public const string Forms = "forms";
}
@@ -59,7 +59,7 @@
public const string Content = "content";
///
- /// alias for the media tree.
+ /// alias for the member tree.
///
public const string Members = "member";
@@ -71,7 +71,7 @@
///
/// alias for the datatype tree.
///
- public const string DataTypes = "datatype";
+ public const string DataTypes = "dataTypes";
///
/// alias for the dictionary tree.
@@ -80,6 +80,22 @@
public const string Stylesheets = "stylesheets";
+ ///
+ /// alias for the document type tree.
+ ///
+ public const string DocumentTypes = "documentTypes";
+
+ ///
+ /// alias for the media type tree.
+ ///
+ public const string MediaTypes = "mediaTypes";
+
+
+ ///
+ /// alias for the member type tree.
+ ///
+ public const string MemberTypes = "memberTypes";
+
///
/// alias for the template tree.
///
diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs
index 3456ff1cfa..7e2bb88964 100644
--- a/src/Umbraco.Core/Constants-Conventions.cs
+++ b/src/Umbraco.Core/Constants-Conventions.cs
@@ -206,7 +206,7 @@ namespace Umbraco.Core
///
internal const string StandardPropertiesGroupName = "Membership";
- internal static Dictionary GetStandardPropertyTypeStubs()
+ public static Dictionary GetStandardPropertyTypeStubs()
{
return new Dictionary
{
diff --git a/src/Umbraco.Core/Constants-Icons.cs b/src/Umbraco.Core/Constants-Icons.cs
new file mode 100644
index 0000000000..a0928919f0
--- /dev/null
+++ b/src/Umbraco.Core/Constants-Icons.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core
+{
+ public static partial class Constants
+ {
+ public static class Icons
+ {
+
+
+ ///
+ /// System contenttype icon
+ ///
+ public const string ContentType = "icon-arrangement";
+
+ ///
+ /// System datatype icon
+ ///
+ public const string DataType = "icon-autofill";
+
+ ///
+ /// System property editor icon
+ ///
+ public const string PropertyEditor = "icon-autofill";
+
+ ///
+ /// System macro icon
+ ///
+ public const string Macro = "icon-settings-alt";
+
+ ///
+ /// System member icon
+ ///
+ public const string Member = "icon-user";
+
+ ///
+ /// System member icon
+ ///
+ public const string MemberType = "icon-users";
+
+
+ ///
+ /// System member icon
+ ///
+ public const string Template = "icon-layout";
+
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs
index eecf0fb1fc..7ec45db7be 100644
--- a/src/Umbraco.Core/Constants-ObjectTypes.cs
+++ b/src/Umbraco.Core/Constants-ObjectTypes.cs
@@ -9,6 +9,36 @@ namespace Umbraco.Core
///
public static class ObjectTypes
{
+ ///
+ /// Guid for a data type container
+ ///
+ public const string DataTypeContainer = "521231E3-8B37-469C-9F9D-51AFC91FEB7B";
+
+ ///
+ /// Guid for a data type container
+ ///
+ public static readonly Guid DataTypeContainerGuid = new Guid(DataTypeContainer);
+
+ ///
+ /// Guid for a doc type container
+ ///
+ public const string DocumentTypeContainer = "2F7A2769-6B0B-4468-90DD-AF42D64F7F16";
+
+ ///
+ /// Guid for a doc type container
+ ///
+ public static readonly Guid DocumentTypeContainerGuid = new Guid(DocumentTypeContainer);
+
+ ///
+ /// Guid for a doc type container
+ ///
+ public const string MediaTypeContainer = "42AEF799-B288-4744-9B10-BE144B73CDC4";
+
+ ///
+ /// Guid for a doc type container
+ ///
+ public static readonly Guid MediaTypeContainerGuid = new Guid(MediaTypeContainer);
+
///
/// Guid for a Content Item object.
///
@@ -29,6 +59,11 @@ namespace Umbraco.Core
///
public const string DataType = "30A2A501-1978-4DDB-A57B-F7EFED43BA3C";
+ ///
+ /// Guid for a DataType object.
+ ///
+ public static readonly Guid DataTypeGuid = new Guid(DataType);
+
///
/// Guid for a Document object.
///
@@ -39,6 +74,11 @@ namespace Umbraco.Core
///
public const string DocumentType = "A2CB7800-F571-4787-9638-BC48539A0EFB";
+ ///
+ /// Guid for a Document Type object.
+ ///
+ public static readonly Guid DocumentTypeGuid = new Guid(DocumentType);
+
///
/// Guid for a Media object.
///
@@ -54,6 +94,11 @@ namespace Umbraco.Core
///
public const string MediaType = "4EA4382B-2F5A-4C2B-9587-AE9B3CF3602E";
+ ///
+ /// Guid for a Media Type object.
+ ///
+ public static readonly Guid MediaTypeGuid = new Guid(MediaType);
+
///
/// Guid for a Member object.
///
@@ -69,6 +114,11 @@ namespace Umbraco.Core
///
public const string MemberType = "9B5416FB-E72F-45A9-A07B-5A9A2709CE43";
+ ///
+ /// Guid for a Member Type object.
+ ///
+ public static readonly Guid MemberTypeGuid = new Guid(MemberType);
+
///
/// Guid for a Stylesheet object.
///
@@ -92,6 +142,11 @@ namespace Umbraco.Core
/// Guid for a Lock object.
///
public const string LockObject = "87A9F1FF-B1E4-4A25-BABB-465A4A47EC41";
+
+ ///
+ /// Guid for a Lock object.
+ ///
+ public static readonly Guid LockObjectGuid = new Guid(LockObject);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs
index a719e845b1..80f118b58e 100644
--- a/src/Umbraco.Core/Constants-PropertyEditors.cs
+++ b/src/Umbraco.Core/Constants-PropertyEditors.cs
@@ -11,7 +11,7 @@ namespace Umbraco.Core
{
///
/// Used to prefix generic properties that are internal content properties
- ///
+ ///
public const string InternalGenericPropertiesPrefix = "_umb_";
///
@@ -74,7 +74,7 @@ namespace Umbraco.Core
///
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string DictionaryPicker = "17B70066-F764-407D-AB05-3717F1E1C513";
-
+
///
/// Guid for the Dropdown list datatype.
///
@@ -158,6 +158,11 @@ namespace Umbraco.Core
///
public const string IntegerAlias = "Umbraco.Integer";
+ ///
+ /// Alias for the Decimal datatype.
+ ///
+ public const string DecimalAlias = "Umbraco.Decimal";
+
///
/// Alias for the listview datatype.
///
@@ -310,13 +315,13 @@ namespace Umbraco.Core
public const string TextboxAlias = "Umbraco.Textbox";
///
- /// Guid for the Textbox multiple datatype.
+ /// Guid for the Textarea datatype.
///
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string TextboxMultiple = "67DB8357-EF57-493E-91AC-936D305E0F2A";
///
- /// Alias for the Textbox multiple datatype.
+ /// Alias for the Textarea datatype.
///
public const string TextboxMultipleAlias = "Umbraco.TextboxMultiple";
@@ -347,7 +352,7 @@ namespace Umbraco.Core
///
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string UltimatePicker = "CDBF0B5D-5CB2-445F-BC12-FCAAEC07CF2C";
-
+
///
/// Guid for the UltraSimpleEditor datatype.
///
@@ -364,7 +369,7 @@ namespace Umbraco.Core
///
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string UmbracoUserControlWrapper = "D15E1281-E456-4B24-AA86-1DDA3E4299D5";
-
+
///
/// Guid for the Upload field datatype.
///
@@ -414,6 +419,15 @@ namespace Umbraco.Core
/// Alias for the email address property editor
///
public const string EmailAddressAlias = "Umbraco.EmailAddress";
+
+ public static class PreValueKeys
+ {
+ ///
+ /// Pre-value name used to indicate a field that can be used to override the database field to which data for the associated
+ /// property is saved
+ ///
+ public const string DataValueType = "umbracoDataValueType";
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs
new file mode 100644
index 0000000000..5dabe90029
--- /dev/null
+++ b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs
@@ -0,0 +1,31 @@
+namespace Umbraco.Core
+{
+ public static partial class Constants
+ {
+ ///
+ /// Defines the identifiers for property-type groups conventions that are used within the Umbraco core.
+ ///
+ public static class PropertyTypeGroups
+ {
+ ///
+ /// Guid for a Image PropertyTypeGroup object.
+ ///
+ public const string Image = "79ED4D07-254A-42CF-8FA9-EBE1C116A596";
+
+ ///
+ /// Guid for a File PropertyTypeGroup object.
+ ///
+ public const string File = "50899F9C-023A-4466-B623-ABA9049885FE";
+
+ ///
+ /// Guid for a Image PropertyTypeGroup object.
+ ///
+ public const string Contents = "79995FA2-63EE-453C-A29B-2E66F324CDBE";
+
+ ///
+ /// Guid for a Image PropertyTypeGroup object.
+ ///
+ public const string Membership = "0756729D-D665-46E3-B84A-37ACEAA614F8";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs
index 13dee96b97..f596820506 100644
--- a/src/Umbraco.Core/Constants-Web.cs
+++ b/src/Umbraco.Core/Constants-Web.cs
@@ -1,4 +1,7 @@
-namespace Umbraco.Core
+using System;
+using System.ComponentModel;
+
+namespace Umbraco.Core
{
public static partial class Constants
{
@@ -7,6 +10,12 @@
///
public static class Web
{
+ public const string UmbracoContextDataToken = "umbraco-context";
+ public const string UmbracoDataToken = "umbraco";
+ public const string PublishedDocumentRequestDataToken = "umbraco-doc-request";
+ public const string CustomRouteDataToken = "umbraco-custom-route";
+ public const string UmbracoRouteDefinitionDataToken = "umbraco-route-def";
+
///
/// The preview cookie name
///
@@ -15,6 +24,8 @@
///
/// The auth cookie name
///
+ [Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string AuthCookieName = "UMB_UCONTEXT";
}
diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs
index b4e3d06273..4da740f458 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreBootManager.cs
@@ -136,7 +136,10 @@ namespace Umbraco.Core
{
try
{
- x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
+ using (ProfilingLogger.DebugDuration(string.Format("Executing {0} in ApplicationInitialized", x.GetType())))
+ {
+ x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
+ }
}
catch (Exception ex)
{
@@ -188,10 +191,16 @@ namespace Umbraco.Core
protected virtual CacheHelper CreateApplicationCache()
{
var cacheHelper = new CacheHelper(
- new ObjectCacheRuntimeCacheProvider(),
+ //we need to have the dep clone runtime cache provider to ensure
+ //all entities are cached properly (cloned in and cloned out)
+ new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()),
new StaticCacheProvider(),
//we have no request based cache when not running in web-based context
- new NullCacheProvider());
+ new NullCacheProvider(),
+ new IsolatedRuntimeCache(type =>
+ //we need to have the dep clone runtime cache provider to ensure
+ //all entities are cached properly (cloned in and cloned out)
+ new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
return cacheHelper;
}
@@ -293,7 +302,10 @@ namespace Umbraco.Core
{
try
{
- x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
+ using (ProfilingLogger.DebugDuration(string.Format("Executing {0} in ApplicationStarting", x.GetType())))
+ {
+ x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
+ }
}
catch (Exception ex)
{
@@ -344,7 +356,10 @@ namespace Umbraco.Core
{
try
{
- x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
+ using (ProfilingLogger.DebugDuration(string.Format("Executing {0} in ApplicationStarted", x.GetType())))
+ {
+ x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
+ }
}
catch (Exception ex)
{
@@ -431,6 +446,7 @@ namespace Umbraco.Core
new Lazy(() => typeof (DelimitedManifestValueValidator)),
new Lazy(() => typeof (EmailValidator)),
new Lazy(() => typeof (IntegerValidator)),
+ new Lazy(() => typeof (DecimalValidator)),
});
//by default we'll use the db server registrar unless the developer has the legacy
diff --git a/src/Umbraco.Core/DisposableTimer.cs b/src/Umbraco.Core/DisposableTimer.cs
index 816256360a..c7e8874449 100644
--- a/src/Umbraco.Core/DisposableTimer.cs
+++ b/src/Umbraco.Core/DisposableTimer.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Web;
@@ -12,6 +13,12 @@ namespace Umbraco.Core
///
public class DisposableTimer : DisposableObject
{
+ private readonly ILogger _logger;
+ private readonly LogType? _logType;
+ private readonly IProfiler _profiler;
+ private readonly Type _loggerType;
+ private readonly string _endMessage;
+ private readonly IDisposable _profilerStep;
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
private readonly Action _callback;
@@ -25,25 +32,12 @@ namespace Umbraco.Core
if (logger == null) throw new ArgumentNullException("logger");
if (loggerType == null) throw new ArgumentNullException("loggerType");
- _callback = x =>
- {
- if (profiler != null)
- {
- profiler.DisposeIfDisposable();
- }
- switch (logType)
- {
- case LogType.Debug:
- logger.Debug(loggerType, () => endMessage + " (took " + x + "ms)");
- break;
- case LogType.Info:
- logger.Info(loggerType, () => endMessage + " (took " + x + "ms)");
- break;
- default:
- throw new ArgumentOutOfRangeException("logType");
- }
-
- };
+ _logger = logger;
+ _logType = logType;
+ _profiler = profiler;
+ _loggerType = loggerType;
+ _endMessage = endMessage;
+
switch (logType)
{
case LogType.Debug:
@@ -58,7 +52,7 @@ namespace Umbraco.Core
if (profiler != null)
{
- profiler.Step(loggerType, startMessage);
+ _profilerStep = profiler.Step(loggerType, startMessage);
}
}
@@ -223,7 +217,36 @@ namespace Umbraco.Core
///
protected override void DisposeResources()
{
- _callback.Invoke(Stopwatch.ElapsedMilliseconds);
+ if (_profiler != null)
+ {
+ _profiler.DisposeIfDisposable();
+ }
+
+ if (_profilerStep != null)
+ {
+ _profilerStep.Dispose();
+ }
+
+ if (_logType.HasValue && _endMessage.IsNullOrWhiteSpace() == false && _loggerType != null && _logger != null)
+ {
+ switch (_logType)
+ {
+ case LogType.Debug:
+ _logger.Debug(_loggerType, () => _endMessage + " (took " + Stopwatch.ElapsedMilliseconds + "ms)");
+ break;
+ case LogType.Info:
+ _logger.Info(_loggerType, () => _endMessage + " (took " + Stopwatch.ElapsedMilliseconds + "ms)");
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("logType");
+ }
+ }
+
+ if (_callback != null)
+ {
+ _callback.Invoke(Stopwatch.ElapsedMilliseconds);
+ }
+
}
}
diff --git a/src/Umbraco.Core/Dynamics/CaseInsensitiveDynamicObject.cs b/src/Umbraco.Core/Dynamics/CaseInsensitiveDynamicObject.cs
new file mode 100644
index 0000000000..43d7adc647
--- /dev/null
+++ b/src/Umbraco.Core/Dynamics/CaseInsensitiveDynamicObject.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Reflection;
+
+namespace Umbraco.Core.Dynamics
+{
+ ///
+ /// This will check enable dynamic access to properties and methods in a case insensitive manner
+ ///
+ ///
+ ///
+ /// This works by using reflection on the type - the reflection lookup is lazy so it will not execute unless a dynamic method needs to be accessed
+ ///
+ public abstract class CaseInsensitiveDynamicObject : DynamicObject
+ where T: class
+ {
+ ///
+ /// Used for dynamic access for case insensitive property access
+ /// `
+ private static readonly Lazy>> CaseInsensitivePropertyAccess = new Lazy>>(() =>
+ {
+ var props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
+ .DistinctBy(x => x.Name);
+ return props.Select(propInfo =>
+ {
+ var name = propInfo.Name.ToLowerInvariant();
+ Func getVal = propInfo.GetValue;
+ return new KeyValuePair>(name, getVal);
+
+ }).ToDictionary(x => x.Key, x => x.Value);
+ });
+
+ ///
+ /// Used for dynamic access for case insensitive property access
+ ///
+ private static readonly Lazy>>> CaseInsensitiveMethodAccess
+ = new Lazy>>>(() =>
+ {
+ var props = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public)
+ .Where(x => x.IsSpecialName == false && x.IsVirtual == false)
+ .DistinctBy(x => x.Name);
+ return props.Select(methodInfo =>
+ {
+ var name = methodInfo.Name.ToLowerInvariant();
+ Func getVal = methodInfo.Invoke;
+ var val = new Tuple>(methodInfo.GetParameters(), getVal);
+ return new KeyValuePair>>(name, val);
+
+ }).ToDictionary(x => x.Key, x => x.Value);
+ });
+
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+ {
+ var name = binder.Name.ToLowerInvariant();
+ if (CaseInsensitiveMethodAccess.Value.ContainsKey(name) == false)
+ return base.TryInvokeMember(binder, args, out result);
+
+ var val = CaseInsensitiveMethodAccess.Value[name];
+ var parameters = val.Item1;
+ var callback = val.Item2;
+ var fullArgs = new List(args);
+ if (args.Length <= parameters.Length)
+ {
+ //need to fill them up if they're optional
+ for (var i = args.Length; i < parameters.Length; i++)
+ {
+ if (parameters[i].IsOptional)
+ {
+ fullArgs.Add(parameters[i].DefaultValue);
+ }
+ }
+ if (fullArgs.Count == parameters.Length)
+ {
+ result = callback((T)(object)this, fullArgs.ToArray());
+ return true;
+ }
+ }
+ return base.TryInvokeMember(binder, args, out result);
+ }
+
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ var name = binder.Name.ToLowerInvariant();
+ if (CaseInsensitivePropertyAccess.Value.ContainsKey(name) == false)
+ return base.TryGetMember(binder, out result);
+
+ result = CaseInsensitivePropertyAccess.Value[name]((T)(object)this);
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs b/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs
index f4ace38465..6af13d6887 100644
--- a/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs
+++ b/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs
@@ -64,7 +64,7 @@ namespace Umbraco.Core.Dynamics
///
public class DynamicXmlConverter : TypeConverter
{
- public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType)
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
var convertableTypes = new[]
{
@@ -78,8 +78,8 @@ namespace Umbraco.Core.Dynamics
typeof(RawXmlDocument)
};
- return convertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, sourceType))
- || base.CanConvertFrom(context, sourceType);
+ return convertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, destinationType))
+ || base.CanConvertFrom(context, destinationType);
}
public override object ConvertTo(
diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs
index 4430d1d74e..9772aaf5bd 100644
--- a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs
+++ b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs
@@ -5,8 +5,10 @@ using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Linq.Expressions;
+using System.Text;
using System.Web.Services.Description;
using Umbraco.Core.Cache;
+using Umbraco.Core.Logging;
namespace Umbraco.Core.Dynamics
{
@@ -20,6 +22,20 @@ namespace Umbraco.Core.Dynamics
///
private static readonly ConcurrentDictionary, MethodInfo[]> MethodCache = new ConcurrentDictionary, MethodInfo[]>();
+ private static IEnumerable GetTypes(Assembly a)
+ {
+ try
+ {
+ return TypeFinder.GetTypesWithFormattedException(a);
+ }
+ catch (ReflectionTypeLoadException ex)
+ {
+ // is this going to flood the log?
+ LogHelper.Error(typeof (ExtensionMethodFinder), "Failed to get types.", ex);
+ return Enumerable.Empty();
+ }
+ }
+
///
/// Returns the enumerable of all extension method info's in the app domain = USE SPARINGLY!!!
///
@@ -36,7 +52,7 @@ namespace Umbraco.Core.Dynamics
// assemblies that contain extension methods
.Where(a => a.IsDefined(typeof (ExtensionAttribute), false))
// types that contain extension methods
- .SelectMany(a => a.GetTypes()
+ .SelectMany(a => GetTypes(a)
.Where(t => t.IsDefined(typeof (ExtensionAttribute), false) && t.IsSealed && t.IsGenericType == false && t.IsNested == false))
// actual extension methods
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public)
@@ -45,9 +61,9 @@ namespace Umbraco.Core.Dynamics
.Concat(typeof (Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
//If we don't do this then we'll be scanning all assemblies each time!
.ToArray(),
-
+
//only cache for 5 minutes
- timeout: TimeSpan.FromMinutes(5),
+ timeout: TimeSpan.FromMinutes(5),
//each time this is accessed it will be for 5 minutes longer
isSliding:true);
@@ -57,7 +73,7 @@ namespace Umbraco.Core.Dynamics
/// Returns all extension methods found matching the definition
///
///
- /// The runtime cache is used to temporarily cache all extension methods found in the app domain so that
+ /// The runtime cache is used to temporarily cache all extension methods found in the app domain so that
/// while we search for individual extension methods, the process will be reasonably 'quick'. We then statically
/// cache the MethodInfo's that we are looking for and then the runtime cache will expire and give back all that memory.
///
@@ -78,7 +94,7 @@ namespace Umbraco.Core.Dynamics
{
var candidates = GetAllExtensionMethodsInAppDomain(runtimeCache);
- // filter by name
+ // filter by name
var filtr1 = candidates.Where(m => m.Name == name);
// filter by args count
@@ -102,7 +118,7 @@ namespace Umbraco.Core.Dynamics
return filtr3.ToArray();
});
-
+
}
private static MethodInfo DetermineMethodFromParams(IEnumerable methods, Type genericType, IEnumerable args)
@@ -123,12 +139,12 @@ namespace Umbraco.Core.Dynamics
types = method.GetParameters().Select(pi => pi.ParameterType).Skip(1)
});
- //This type comparer will check
+ //This type comparer will check
var typeComparer = new DelegateEqualityComparer(
- //Checks if the argument type passed in can be assigned from the parameter type in the method. For
+ //Checks if the argument type passed in can be assigned from the parameter type in the method. For
// example, if the argument type is HtmlHelper but the method parameter type is HtmlHelper then
// it will match because the argument is assignable to that parameter type and will be able to execute
- TypeHelper.IsTypeAssignableFrom,
+ TypeHelper.IsTypeAssignableFrom,
//This will not ever execute but if it does we need to get the hash code of the string because the hash
// code of a type is random
type => type.FullName.GetHashCode());
@@ -159,7 +175,7 @@ namespace Umbraco.Core.Dynamics
.ToArray();
var methods = GetAllExtensionMethods(runtimeCache, thisType, name, args.Length).ToArray();
-
+
return DetermineMethodFromParams(methods, genericType, args);
}
}
diff --git a/src/Umbraco.Core/Events/CancellableEventArgs.cs b/src/Umbraco.Core/Events/CancellableEventArgs.cs
index 506ba1c22e..72cef19f7a 100644
--- a/src/Umbraco.Core/Events/CancellableEventArgs.cs
+++ b/src/Umbraco.Core/Events/CancellableEventArgs.cs
@@ -1,5 +1,8 @@
using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Security.Permissions;
+using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.Events
{
@@ -11,11 +14,19 @@ namespace Umbraco.Core.Events
{
private bool _cancel;
+ public CancellableEventArgs(bool canCancel, EventMessages messages, IDictionary additionalData)
+ {
+ CanCancel = canCancel;
+ Messages = messages;
+ AdditionalData = new ReadOnlyDictionary(additionalData);
+ }
+
public CancellableEventArgs(bool canCancel, EventMessages eventMessages)
{
if (eventMessages == null) throw new ArgumentNullException("eventMessages");
CanCancel = canCancel;
Messages = eventMessages;
+ AdditionalData = new ReadOnlyDictionary(new Dictionary());
}
public CancellableEventArgs(bool canCancel)
@@ -23,6 +34,7 @@ namespace Umbraco.Core.Events
CanCancel = canCancel;
//create a standalone messages
Messages = new EventMessages();
+ AdditionalData = new ReadOnlyDictionary(new Dictionary());
}
public CancellableEventArgs(EventMessages eventMessages)
@@ -78,5 +90,14 @@ namespace Umbraco.Core.Events
/// Returns the EventMessages object which is used to add messages to the message collection for this event
///
public EventMessages Messages { get; private set; }
+
+ ///
+ /// In some cases raised evens might need to contain additional arbitrary readonly data which can be read by event subscribers
+ ///
+ ///
+ /// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility
+ /// so we cannot change the strongly typed nature for some events.
+ ///
+ public ReadOnlyDictionary AdditionalData { get; private set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs
index 726d32d7b0..a05f09ece5 100644
--- a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs
+++ b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Security.Permissions;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -11,8 +12,13 @@ namespace Umbraco.Core.Events
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
public class CancellableObjectEventArgs : CancellableEventArgs
{
+ public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary additionalData)
+ : base(canCancel, messages, additionalData)
+ {
+ EventObject = eventObject;
+ }
- public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages)
+ public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages)
: base(canCancel, eventMessages)
{
EventObject = eventObject;
diff --git a/src/Umbraco.Core/Events/EventExtensions.cs b/src/Umbraco.Core/Events/EventExtensions.cs
index 8c645aead6..700a02457a 100644
--- a/src/Umbraco.Core/Events/EventExtensions.cs
+++ b/src/Umbraco.Core/Events/EventExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Events
diff --git a/src/Umbraco.Core/Events/MigrationEventArgs.cs b/src/Umbraco.Core/Events/MigrationEventArgs.cs
index 7eed61484b..008e50d2ee 100644
--- a/src/Umbraco.Core/Events/MigrationEventArgs.cs
+++ b/src/Umbraco.Core/Events/MigrationEventArgs.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using Semver;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Core.Events
@@ -13,23 +14,49 @@ namespace Umbraco.Core.Events
///
///
///
+ ///
///
///
- public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
- : base(eventObject, canCancel)
- {
- ConfiguredSemVersion = configuredVersion;
- TargetSemVersion = targetVersion;
- }
+ public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel)
+ : this(eventObject, null, configuredVersion, targetVersion, productName, canCancel)
+ { }
- [Obsolete("Use constructor accepting UmbracoVersion instances instead")]
+ ///
+ /// Constructor accepting multiple migrations that are used in the migration runner
+ ///
+ ///
+ ///
+ ///
+ ///
+ [Obsolete("Use constructor accepting a product name instead.")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
+ : this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, canCancel)
+ { }
+
+ [Obsolete("Use constructor accepting SemVersion instances and a product name instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
- : base(eventObject, canCancel)
- {
- ConfiguredSemVersion = new SemVersion(configuredVersion);
- TargetSemVersion = new SemVersion(targetVersion);
- }
+ : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, canCancel)
+ { }
+
+ ///
+ /// Constructor accepting multiple migrations that are used in the migration runner
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal MigrationEventArgs(IList eventObject, MigrationContext migrationContext, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel)
+ : base(eventObject, canCancel)
+ {
+ MigrationContext = migrationContext;
+ ConfiguredSemVersion = configuredVersion;
+ TargetSemVersion = targetVersion;
+ ProductName = productName;
+ }
///
/// Constructor accepting multiple migrations that are used in the migration runner
@@ -39,12 +66,15 @@ namespace Umbraco.Core.Events
///
///
///
+ [Obsolete("Use constructor accepting a product name instead.")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
internal MigrationEventArgs(IList eventObject, MigrationContext migrationContext, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
: base(eventObject, canCancel)
{
MigrationContext = migrationContext;
ConfiguredSemVersion = configuredVersion;
TargetSemVersion = targetVersion;
+ ProductName = GlobalSettings.UmbracoMigrationName;
}
///
@@ -53,21 +83,28 @@ namespace Umbraco.Core.Events
///
///
///
- public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion)
- : base(eventObject)
- {
- ConfiguredSemVersion = configuredVersion;
- TargetSemVersion = targetVersion;
- }
+ ///
+ public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, string productName)
+ : this(eventObject, null, configuredVersion, targetVersion, productName, false)
+ { }
- [Obsolete("Use constructor accepting UmbracoVersion instances instead")]
+ ///
+ /// Constructor accepting multiple migrations that are used in the migration runner
+ ///
+ ///
+ ///
+ ///
+ [Obsolete("Use constructor accepting a product name instead.")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion)
+ : this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, false)
+ { }
+
+ [Obsolete("Use constructor accepting SemVersion instances and a product name instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion)
- : base(eventObject)
- {
- ConfiguredSemVersion = new SemVersion(configuredVersion);
- TargetSemVersion = new SemVersion(targetVersion);
- }
+ : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, false)
+ { }
///
/// Returns all migrations that were used in the migration runner
@@ -95,6 +132,8 @@ namespace Umbraco.Core.Events
public SemVersion TargetSemVersion { get; private set; }
+ public string ProductName { get; private set; }
+
internal MigrationContext MigrationContext { get; private set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/SaveEventArgs.cs b/src/Umbraco.Core/Events/SaveEventArgs.cs
index b84e28285e..dafd326e1c 100644
--- a/src/Umbraco.Core/Events/SaveEventArgs.cs
+++ b/src/Umbraco.Core/Events/SaveEventArgs.cs
@@ -4,6 +4,18 @@ namespace Umbraco.Core.Events
{
public class SaveEventArgs : CancellableObjectEventArgs>
{
+ ///
+ /// Constructor accepting multiple entities that are used in the saving operation
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SaveEventArgs(IEnumerable eventObject, bool canCancel, EventMessages messages, IDictionary additionalData)
+ : base(eventObject, canCancel, messages, additionalData)
+ {
+ }
+
///
/// Constructor accepting multiple entities that are used in the saving operation
///
@@ -25,12 +37,24 @@ namespace Umbraco.Core.Events
{
}
- ///
- /// Constructor accepting a single entity instance
- ///
- ///
- ///
- public SaveEventArgs(TEntity eventObject, EventMessages eventMessages)
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SaveEventArgs(TEntity eventObject, bool canCancel, EventMessages messages, IDictionary additionalData)
+ : base(new List { eventObject }, canCancel, messages, additionalData)
+ {
+ }
+
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ public SaveEventArgs(TEntity eventObject, EventMessages eventMessages)
: base(new List { eventObject }, eventMessages)
{
}
diff --git a/src/Umbraco.Core/Exceptions/DataOperationException.cs b/src/Umbraco.Core/Exceptions/DataOperationException.cs
new file mode 100644
index 0000000000..9a66e6a5be
--- /dev/null
+++ b/src/Umbraco.Core/Exceptions/DataOperationException.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Umbraco.Core.Exceptions
+{
+ internal class DataOperationException : Exception
+ {
+ public T Operation { get; private set; }
+
+ public DataOperationException(T operation, string message)
+ :base(message)
+ {
+ Operation = operation;
+ }
+
+ public DataOperationException(T operation)
+ : base("Data operation exception: " + operation)
+ {
+ Operation = operation;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs b/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs
index fcf7a44677..bb9becf058 100644
--- a/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs
+++ b/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs
@@ -3,22 +3,41 @@
namespace Umbraco.Core.Exceptions
{
public class InvalidCompositionException : Exception
- {
- public string ContentTypeAlias { get; set; }
+ {
+ public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliass)
+ {
+ ContentTypeAlias = contentTypeAlias;
+ AddedCompositionAlias = addedCompositionAlias;
+ PropertyTypeAliases = propertyTypeAliass;
+ }
- public string AddedCompositionAlias { get; set; }
+ public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliass)
+ {
+ ContentTypeAlias = contentTypeAlias;
+ PropertyTypeAliases = propertyTypeAliass;
+ }
- public string PropertyTypeAlias { get; set; }
+ public string ContentTypeAlias { get; private set; }
+
+ public string AddedCompositionAlias { get; private set; }
+
+ public string[] PropertyTypeAliases { get; private set; }
public override string Message
{
get
{
- return string.Format(
- "InvalidCompositionException - ContentType with alias '{0}' was added as a Compsition to ContentType with alias '{1}', " +
- "but there was a conflict on the PropertyType alias '{2}'. " +
+ return AddedCompositionAlias.IsNullOrWhiteSpace()
+ ? string.Format(
+ "ContentType with alias '{0}' has an invalid composition " +
+ "and there was a conflict on the following PropertyTypes: '{1}'. " +
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
- AddedCompositionAlias, ContentTypeAlias, PropertyTypeAlias);
+ ContentTypeAlias, string.Join(", ", PropertyTypeAliases))
+ : string.Format(
+ "ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
+ "but there was a conflict on the following PropertyTypes: '{2}'. " +
+ "PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
+ AddedCompositionAlias, ContentTypeAlias, string.Join(", ", PropertyTypeAliases));
}
}
}
diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs
index 433d01c206..98fdb01ff4 100644
--- a/src/Umbraco.Core/IO/SystemDirectories.cs
+++ b/src/Umbraco.Core/IO/SystemDirectories.cs
@@ -154,7 +154,7 @@ namespace Umbraco.Core.IO
{
get
{
- return IOHelper.ReturnPath("umbracoWebservicesPath", "~/umbraco/webservices");
+ return IOHelper.ReturnPath("umbracoWebservicesPath", Umbraco.EnsureEndsWith("/") + "webservices");
}
}
diff --git a/src/Umbraco.Core/IO/UmbracoMediaFile.cs b/src/Umbraco.Core/IO/UmbracoMediaFile.cs
index b8fe310f54..d708fcb804 100644
--- a/src/Umbraco.Core/IO/UmbracoMediaFile.cs
+++ b/src/Umbraco.Core/IO/UmbracoMediaFile.cs
@@ -199,8 +199,8 @@ namespace Umbraco.Core.IO
using (var image = Image.FromStream(fs))
{
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
- ? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
- : string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
+ ? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
+ : string.Format("{0}_{1}." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
var thumbnail = maxWidthHeight == -1
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
diff --git a/src/Umbraco.Core/IO/ViewHelper.cs b/src/Umbraco.Core/IO/ViewHelper.cs
index 28dad49dc0..933a9d2956 100644
--- a/src/Umbraco.Core/IO/ViewHelper.cs
+++ b/src/Umbraco.Core/IO/ViewHelper.cs
@@ -7,7 +7,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.IO
{
- internal class ViewHelper
+ public class ViewHelper
{
private readonly IFileSystem _viewFileSystem;
@@ -66,17 +66,58 @@ namespace Umbraco.Core.IO
return viewContent;
}
- internal static string GetDefaultFileContent(string layoutPageAlias = null)
+ public static string GetDefaultFileContent(string layoutPageAlias = null, string modelClassName = null, string modelNamespace = null, string modelNamespaceAlias = null)
{
- var design = @"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
-@{
- Layout = null;
-}";
+ var content = new StringBuilder();
- if (layoutPageAlias.IsNullOrWhiteSpace() == false)
- design = design.Replace("null", string.Format("\"{0}.cshtml\"", layoutPageAlias));
+ if (string.IsNullOrWhiteSpace(modelNamespaceAlias))
+ modelNamespaceAlias = "ContentModels";
- return design;
+ // either
+ // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
+ // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
+ // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
+ content.Append("@inherits Umbraco.Web.Mvc.UmbracoTemplatePage");
+ if (modelClassName.IsNullOrWhiteSpace() == false)
+ {
+ content.Append("<");
+ if (modelNamespace.IsNullOrWhiteSpace() == false)
+ {
+ content.Append(modelNamespaceAlias);
+ content.Append(".");
+ }
+ content.Append(modelClassName);
+ content.Append(">");
+ }
+ content.Append("\r\n");
+
+ // if required, add
+ // @using ContentModels = ModelNamespace;
+ if (modelClassName.IsNullOrWhiteSpace() == false && modelNamespace.IsNullOrWhiteSpace() == false)
+ {
+ content.Append("@using ");
+ content.Append(modelNamespaceAlias);
+ content.Append(" = ");
+ content.Append(modelNamespace);
+ content.Append(";\r\n");
+ }
+
+ // either
+ // Layout = null;
+ // Layout = "layoutPage.cshtml";
+ content.Append("@{\r\n\tLayout = ");
+ if (layoutPageAlias.IsNullOrWhiteSpace())
+ {
+ content.Append("null");
+ }
+ else
+ {
+ content.Append("\"");
+ content.Append(layoutPageAlias);
+ content.Append(".cshtml\"");
+ }
+ content.Append(";\r\n}");
+ return content.ToString();
}
private string SaveTemplateToFile(ITemplate template)
diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs
index 850e3845af..66f8a8a568 100644
--- a/src/Umbraco.Core/Macros/MacroTagParser.cs
+++ b/src/Umbraco.Core/Macros/MacroTagParser.cs
@@ -11,8 +11,12 @@ namespace Umbraco.Core.Macros
///
internal class MacroTagParser
{
- private static readonly Regex MacroRteContent = new Regex(@"()", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
- private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO (?:.+?)?macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private static readonly Regex MacroRteContent = new Regex(@"()",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
+
+ private static readonly Regex MacroPersistedFormat =
+ new Regex(@"(<\?UMBRACO_MACRO (?:.+?)??macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?\?UMBRACO_MACRO>)",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
///
/// This formats the persisted string to something useful for the rte so that the macro renders properly since we
diff --git a/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs b/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs
index 452c05adb8..1be504ea45 100644
--- a/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs
+++ b/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs
@@ -75,7 +75,7 @@ namespace Umbraco.Core.Media.Exif
{
if (items.ContainsKey (key))
items.Remove (key);
- if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
+ if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
items.Add (key, new WindowsByteString (key, value));
} else {
items.Add (key, new ExifAscii (key, value, parent.Encoding));
diff --git a/src/Umbraco.Core/Media/ImageHelper.cs b/src/Umbraco.Core/Media/ImageHelper.cs
index 5842bb67bb..99fc278e61 100644
--- a/src/Umbraco.Core/Media/ImageHelper.cs
+++ b/src/Umbraco.Core/Media/ImageHelper.cs
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Media
var fileHeight = image.Height;
return new Size(fileWidth, fileHeight);
}
-
+
}
public static string GetMimeType(this Image image)
@@ -79,22 +79,22 @@ namespace Umbraco.Core.Media
///
///
internal static IEnumerable GenerateMediaThumbnails(
- IFileSystem fs,
- string fileName,
- string extension,
+ IFileSystem fs,
+ string fileName,
+ string extension,
Image originalImage,
IEnumerable additionalThumbSizes)
{
var result = new List();
- var allSizesDictionary = new Dictionary {{100,"thumb"}, {500,"big-thumb"}};
-
+ var allSizesDictionary = new Dictionary { { 100, "thumb" }, { 500, "big-thumb" } };
+
//combine the static dictionary with the additional sizes with only unique values
var allSizes = allSizesDictionary.Select(kv => kv.Key)
.Union(additionalThumbSizes.Where(x => x > 0).Distinct());
- var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s]: "");
+ var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s] : "");
foreach (var s in sizesDictionary)
{
@@ -121,9 +121,9 @@ namespace Umbraco.Core.Media
///
private static ResizedImage Resize(IFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage)
{
- var fileNameThumb = String.IsNullOrEmpty(fileNameAddition)
- ? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf(".")))
- : string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition);
+ var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
+ ? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)))
+ : string.Format("{0}_{1}." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
var thumb = GenerateThumbnail(
originalImage,
@@ -190,9 +190,9 @@ namespace Umbraco.Core.Media
//use best quality
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
}
-
- g.SmoothingMode = SmoothingMode.HighQuality;
+
+ g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
@@ -202,10 +202,29 @@ namespace Umbraco.Core.Media
// Copy metadata
var imageEncoders = ImageCodecInfo.GetImageEncoders();
-
- var codec = extension.ToLower() == "png" || extension.ToLower() == "gif"
- ? imageEncoders.Single(t => t.MimeType.Equals("image/png"))
- : imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
+ ImageCodecInfo codec;
+ switch (extension.ToLower())
+ {
+ case "png":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
+ break;
+ case "gif":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/gif"));
+ break;
+ case "tif":
+ case "tiff":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/tiff"));
+ break;
+ case "bmp":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/bmp"));
+ break;
+ // TODO: this is dirty, defaulting to jpg but the return value of this thing is used all over the
+ // place so left it here, but it needs to not set a codec if it doesn't know which one to pick
+ // Note: when fixing this: both .jpg and .jpeg should be handled as extensions
+ default:
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
+ break;
+ }
// Set compresion ratio to 90%
var ep = new EncoderParameters();
@@ -213,12 +232,14 @@ namespace Umbraco.Core.Media
// Save the new image using the dimensions of the image
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
+ var predictableThumbnailNameJpg = predictableThumbnailName.Substring(0, predictableThumbnailName.LastIndexOf(".", StringComparison.Ordinal)) + ".jpg";
using (var ms = new MemoryStream())
{
bp.Save(ms, codec, ep);
ms.Seek(0, 0);
fs.AddFile(predictableThumbnailName, ms);
+ fs.AddFile(predictableThumbnailNameJpg, ms);
}
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 2238260611..2b20b68164 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
@@ -155,6 +156,7 @@ namespace Umbraco.Core.Models
/// Language of the data contained within this Content object.
///
[Obsolete("This is not used and will be removed from the codebase in future versions")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public string Language
{
get { return _language; }
@@ -340,18 +342,6 @@ namespace Umbraco.Core.Models
}
}
- ///
- /// Method to call when Entity is being saved
- ///
- /// Created date is set and a Unique key is assigned
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- if(Key == Guid.Empty)
- Key = Guid.NewGuid();
- }
-
///
/// Method to call when Entity is being updated
///
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 6beead06e6..b3d0f693d9 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -367,8 +367,27 @@ namespace Umbraco.Core.Models
/// Value to set for the Property
public virtual void SetPropertyValue(string propertyTypeAlias, long value)
{
- string val = value.ToString();
- SetValueOnProperty(propertyTypeAlias, val);
+ SetValueOnProperty(propertyTypeAlias, value);
+ }
+
+ ///
+ /// Sets the value of a Property
+ ///
+ /// Alias of the PropertyType
+ /// Value to set for the Property
+ public virtual void SetPropertyValue(string propertyTypeAlias, decimal value)
+ {
+ SetValueOnProperty(propertyTypeAlias, value);
+ }
+
+ ///
+ /// Sets the value of a Property
+ ///
+ /// Alias of the PropertyType
+ /// Value to set for the Property
+ public virtual void SetPropertyValue(string propertyTypeAlias, double value)
+ {
+ SetValueOnProperty(propertyTypeAlias, value);
}
///
diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index 88b6308c29..e91996e32a 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -560,8 +560,7 @@ namespace Umbraco.Core.Models
//Additional thumbnails configured as prevalues on the DataType
if (thumbnailSizes != null)
{
- var sep = (thumbnailSizes.Contains("") == false && thumbnailSizes.Contains(",")) ? ',' : ';';
- foreach (var thumb in thumbnailSizes.Split(sep))
+ foreach (var thumb in thumbnailSizes.Split(new[] { ";", "," }, StringSplitOptions.RemoveEmptyEntries))
{
int thumbSize;
if (thumb != "" && int.TryParse(thumb, out thumbSize))
diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs
index 355d724fbe..0926e48e31 100644
--- a/src/Umbraco.Core/Models/ContentType.cs
+++ b/src/Umbraco.Core/Models/ContentType.cs
@@ -53,6 +53,9 @@ namespace Umbraco.Core.Models
///
/// Gets or sets the alias of the default Template.
+ /// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
+ /// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
+ /// we should not store direct entity
///
[IgnoreDataMember]
public ITemplate DefaultTemplate
@@ -79,6 +82,9 @@ namespace Umbraco.Core.Models
///
/// Gets or Sets a list of Templates which are allowed for the ContentType
+ /// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
+ /// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
+ /// we should not store direct entity
///
[DataMember]
public IEnumerable AllowedTemplates
@@ -137,19 +143,6 @@ namespace Umbraco.Core.Models
return result;
}
- ///
- /// Method to call when Entity is being saved
- ///
- /// Created date is set and a Unique key is assigned
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- if (Key == Guid.Empty)
- Key = Guid.NewGuid();
- }
-
-
///
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
///
diff --git a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs
new file mode 100644
index 0000000000..b1d2b45dc5
--- /dev/null
+++ b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs
@@ -0,0 +1,17 @@
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Used when determining available compositions for a given content type
+ ///
+ internal class ContentTypeAvailableCompositionsResult
+ {
+ public ContentTypeAvailableCompositionsResult(IContentTypeComposition composition, bool allowed)
+ {
+ Composition = composition;
+ Allowed = allowed;
+ }
+
+ public IContentTypeComposition Composition { get; private set; }
+ public bool Allowed { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs
new file mode 100644
index 0000000000..653d7a10a9
--- /dev/null
+++ b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Used when determining available compositions for a given content type
+ ///
+ internal class ContentTypeAvailableCompositionsResults
+ {
+ public ContentTypeAvailableCompositionsResults()
+ {
+ Ancestors = Enumerable.Empty();
+ Results = Enumerable.Empty();
+ }
+
+ public ContentTypeAvailableCompositionsResults(IEnumerable ancestors, IEnumerable results)
+ {
+ Ancestors = ancestors;
+ Results = results;
+ }
+
+ public IEnumerable Ancestors { get; private set; }
+ public IEnumerable Results { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index c1c8e3270c..a0305d2cfb 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Models
private string _alias;
private string _description;
private int _sortOrder;
- private string _icon = "folder.png";
+ private string _icon = "icon-folder";
private string _thumbnail = "folder.png";
private int _creatorId;
private bool _allowedAsRoot;
@@ -373,43 +373,35 @@ namespace Umbraco.Core.Models
{
_propertyGroups = value;
_propertyGroups.CollectionChanged += PropertyGroupsChanged;
+ PropertyGroupsChanged(_propertyGroups, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
///
- /// List of PropertyTypes available on this ContentType.
- /// This list aggregates PropertyTypes across the PropertyGroups.
+ /// Gets all property types, across all property groups.
///
- ///
- ///
- /// The setter is used purely to set the property types that DO NOT belong to a group!
- ///
- /// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
- /// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
- /// data. We manually clone this instead.
- ///
[IgnoreDataMember]
[DoNotClone]
public virtual IEnumerable PropertyTypes
{
get
{
- var types = _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes));
- return types;
- }
- internal set
- {
- _propertyTypes = new PropertyTypeCollection(value);
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
+ return _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes));
}
}
///
- /// Returns the property type collection containing types that are non-groups - used for tests
+ /// Gets or sets the property types that are not in a group.
///
- internal IEnumerable NonGroupedPropertyTypes
+ public IEnumerable NoGroupPropertyTypes
{
get { return _propertyTypes; }
+ set
+ {
+ _propertyTypes = new PropertyTypeCollection(value);
+ _propertyTypes.CollectionChanged += PropertyTypesChanged;
+ PropertyTypesChanged(_propertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
}
///
@@ -460,11 +452,6 @@ namespace Umbraco.Core.Models
/// Returns True if PropertyType was added, otherwise False
public bool AddPropertyType(PropertyType propertyType)
{
- if (propertyType.HasIdentity == false)
- {
- propertyType.Key = Guid.NewGuid();
- }
-
if (PropertyTypeExists(propertyType.Alias) == false)
{
_propertyTypes.Add(propertyType);
@@ -480,24 +467,34 @@ namespace Umbraco.Core.Models
/// Alias of the PropertyType to move
/// Name of the PropertyGroup to move the PropertyType to
///
+ /// If is null then the property is moved back to
+ /// "generic properties" ie does not have a tab anymore.
public bool MovePropertyType(string propertyTypeAlias, string propertyGroupName)
{
- if (PropertyTypes.Any(x => x.Alias == propertyTypeAlias) == false || PropertyGroups.Any(x => x.Name == propertyGroupName) == false)
- return false;
+ // note: not dealing with alias casing at all here?
- var propertyType = PropertyTypes.First(x => x.Alias == propertyTypeAlias);
- //The PropertyType already belongs to a PropertyGroup, so we have to remove the PropertyType from that group
- if (PropertyGroups.Any(x => x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias)))
- {
- var oldPropertyGroup = PropertyGroups.First(x => x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias));
+ // get property, ensure it exists
+ var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias);
+ if (propertyType == null) return false;
+
+ // get new group, if required, and ensure it exists
+ var newPropertyGroup = propertyGroupName == null
+ ? null
+ : PropertyGroups.FirstOrDefault(x => x.Name == propertyGroupName);
+ if (propertyGroupName != null && newPropertyGroup == null) return false;
+
+ // get old group
+ var oldPropertyGroup = PropertyGroups.FirstOrDefault(x =>
+ x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias));
+
+ // set new group
+ propertyType.PropertyGroupId = newPropertyGroup == null ? null : new Lazy(() => newPropertyGroup.Id, false);
+
+ // remove from old group, if any - add to new group, if any
+ if (oldPropertyGroup != null)
oldPropertyGroup.PropertyTypes.RemoveItem(propertyTypeAlias);
- }
-
- propertyType.PropertyGroupId = new Lazy(() => default(int));
- propertyType.ResetDirtyProperties();
-
- var propertyGroup = PropertyGroups.First(x => x.Name == propertyGroupName);
- propertyGroup.PropertyTypes.Add(propertyType);
+ if (newPropertyGroup != null)
+ newPropertyGroup.PropertyTypes.Add(propertyType);
return true;
}
@@ -533,6 +530,18 @@ namespace Umbraco.Core.Models
/// Name of the to remove
public void RemovePropertyGroup(string propertyGroupName)
{
+ // if no group exists with that name, do nothing
+ var group = PropertyGroups[propertyGroupName];
+ if (group == null) return;
+
+ // re-assign the group's properties to no group
+ foreach (var property in group.PropertyTypes)
+ {
+ property.PropertyGroupId = null;
+ _propertyTypes.Add(property);
+ }
+
+ // actually remove the group
PropertyGroups.RemoveItem(propertyGroupName);
OnPropertyChanged(PropertyGroupCollectionSelector);
}
diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
index d102f06483..4cf4a08bf1 100644
--- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
@@ -23,8 +23,8 @@ namespace Umbraco.Core.Models
protected ContentTypeCompositionBase(IContentTypeComposition parent)
: this(parent, null)
- {
- }
+ {
+ }
protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias)
: base(parent, alias)
@@ -37,16 +37,21 @@ namespace Umbraco.Core.Models
x => x.ContentTypeComposition);
///
- /// List of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType
+ /// Gets or sets the content types that compose this content type.
///
[DataMember]
public IEnumerable ContentTypeComposition
{
get { return _contentTypeComposition; }
+ set
+ {
+ _contentTypeComposition = value.ToList();
+ OnPropertyChanged(ContentTypeCompositionSelector);
+ }
}
///
- /// Returns a list of objects from the composition
+ /// Gets the property groups for the entire composition.
///
[IgnoreDataMember]
public IEnumerable CompositionPropertyGroups
@@ -59,7 +64,7 @@ namespace Umbraco.Core.Models
}
///
- /// Returns a list of objects from the composition
+ /// Gets the property types for the entire composition.
///
[IgnoreDataMember]
public IEnumerable CompositionPropertyTypes
@@ -72,10 +77,10 @@ namespace Umbraco.Core.Models
}
///
- /// Adds a new ContentType to the list of composite ContentTypes
+ /// Adds a content type to the composition.
///
- /// to add
- /// True if ContentType was added, otherwise returns False
+ /// The content type to add.
+ /// True if the content type was added, otherwise false.
public bool AddContentType(IContentTypeComposition contentType)
{
if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists)))
@@ -94,13 +99,7 @@ namespace Umbraco.Core.Models
.Select(p => p.Alias)).ToList();
if (conflictingPropertyTypeAliases.Any())
- throw new InvalidCompositionException
- {
- AddedCompositionAlias = contentType.Alias,
- ContentTypeAlias = Alias,
- PropertyTypeAlias =
- string.Join(", ", conflictingPropertyTypeAliases)
- };
+ throw new InvalidCompositionException(Alias, contentType.Alias, conflictingPropertyTypeAliases.ToArray());
_contentTypeComposition.Add(contentType);
OnPropertyChanged(ContentTypeCompositionSelector);
@@ -110,10 +109,10 @@ namespace Umbraco.Core.Models
}
///
- /// Removes a ContentType with the supplied alias from the the list of composite ContentTypes
+ /// Removes a content type with a specified alias from the composition.
///
- /// Alias of a
- /// True if ContentType was removed, otherwise returns False
+ /// The alias of the content type to remove.
+ /// True if the content type was removed, otherwise false.
public bool RemoveContentType(string alias)
{
if (ContentTypeCompositionExists(alias))
@@ -123,10 +122,10 @@ namespace Umbraco.Core.Models
return false;
RemovedContentTypeKeyTracker.Add(contentTypeComposition.Id);
-
+
//If the ContentType we are removing has Compositions of its own these needs to be removed as well
var compositionIdsToRemove = contentTypeComposition.CompositionIds().ToList();
- if(compositionIdsToRemove.Any())
+ if (compositionIdsToRemove.Any())
RemovedContentTypeKeyTracker.AddRange(compositionIdsToRemove);
OnPropertyChanged(ContentTypeCompositionSelector);
@@ -163,31 +162,44 @@ namespace Umbraco.Core.Models
///
/// Adds a PropertyGroup.
- /// This method will also check if a group already exists with the same name and link it to the parent.
///
/// Name of the PropertyGroup to add
/// Returns True if a PropertyGroup with the passed in name was added, otherwise False
public override bool AddPropertyGroup(string groupName)
{
- if (PropertyGroups.Any(x => x.Name == groupName))
- return false;
+ return AddAndReturnPropertyGroup(groupName) != null;
+ }
- var propertyGroup = new PropertyGroup {Name = groupName, SortOrder = 0};
+ private PropertyGroup AddAndReturnPropertyGroup(string name)
+ {
+ // ensure we don't have it already
+ if (PropertyGroups.Any(x => x.Name == name))
+ return null;
- if (CompositionPropertyGroups.Any(x => x.Name == groupName))
+ // create the new group
+ var group = new PropertyGroup { Name = name, SortOrder = 0 };
+
+ // check if it is inherited - there might be more than 1 but we want the 1st, to
+ // reuse its sort order - if there are more than 1 and they have different sort
+ // orders... there isn't much we can do anyways
+ var inheritGroup = CompositionPropertyGroups.FirstOrDefault(x => x.Name == name);
+ if (inheritGroup == null)
{
- var firstGroup = CompositionPropertyGroups.First(x => x.Name == groupName && x.ParentId.HasValue == false);
- propertyGroup.SetLazyParentId(new Lazy(() => firstGroup.Id));
+ // no, just local, set sort order
+ var lastGroup = PropertyGroups.LastOrDefault();
+ if (lastGroup != null)
+ group.SortOrder = lastGroup.SortOrder + 1;
+ }
+ else
+ {
+ // yes, inherited, re-use sort order
+ group.SortOrder = inheritGroup.SortOrder;
}
- if (PropertyGroups.Any())
- {
- var last = PropertyGroups.Last();
- propertyGroup.SortOrder = last.SortOrder + 1;
- }
+ // add
+ PropertyGroups.Add(group);
- PropertyGroups.Add(propertyGroup);
- return true;
+ return group;
}
///
@@ -198,39 +210,22 @@ namespace Umbraco.Core.Models
/// Returns True if PropertyType was added, otherwise False
public override bool AddPropertyType(PropertyType propertyType, string propertyGroupName)
{
- if (propertyType.HasIdentity == false)
- {
- propertyType.Key = Guid.NewGuid();
- }
+ // ensure no duplicate alias - over all composition properties
+ if (PropertyTypeExists(propertyType.Alias))
+ return false;
- if (PropertyTypeExists(propertyType.Alias) == false)
- {
- if (PropertyGroups.Contains(propertyGroupName))
- {
- propertyType.PropertyGroupId = new Lazy(() => PropertyGroups[propertyGroupName].Id);
- PropertyGroups[propertyGroupName].PropertyTypes.Add(propertyType);
- }
- else
- {
- //If the PropertyGroup doesn't already exist we create a new one
- var propertyTypes = new List { propertyType };
- var propertyGroup = new PropertyGroup(new PropertyTypeCollection(propertyTypes)) { Name = propertyGroupName, SortOrder = 1 };
- //and check if its an inherited PropertyGroup, which exists in the composition
- if (CompositionPropertyGroups.Any(x => x.Name == propertyGroupName))
- {
- var parentPropertyGroup = CompositionPropertyGroups.First(x => x.Name == propertyGroupName && x.ParentId.HasValue == false);
- propertyGroup.SortOrder = parentPropertyGroup.SortOrder;
- //propertyGroup.ParentId = parentPropertyGroup.Id;
- propertyGroup.SetLazyParentId(new Lazy(() => parentPropertyGroup.Id));
- }
+ // get and ensure a group local to this content type
+ var group = PropertyGroups.Contains(propertyGroupName)
+ ? PropertyGroups[propertyGroupName]
+ : AddAndReturnPropertyGroup(propertyGroupName);
+ if (group == null)
+ return false;
- PropertyGroups.Add(propertyGroup);
- }
+ // add property to group
+ propertyType.PropertyGroupId = new Lazy(() => group.Id);
+ group.PropertyTypes.Add(propertyType);
- return true;
- }
-
- return false;
+ return true;
}
///
diff --git a/src/Umbraco.Core/Models/DataTypeDatabaseType.cs b/src/Umbraco.Core/Models/DataTypeDatabaseType.cs
index e45cdd1a73..2fccdc0645 100644
--- a/src/Umbraco.Core/Models/DataTypeDatabaseType.cs
+++ b/src/Umbraco.Core/Models/DataTypeDatabaseType.cs
@@ -6,10 +6,6 @@ namespace Umbraco.Core.Models
///
/// Enum of the various DbTypes for which the Property values are stored
///
- ///
- /// Object is added to support complex values from PropertyEditors,
- /// but will be saved under the Ntext column.
- ///
[Serializable]
[DataContract]
public enum DataTypeDatabaseType
@@ -21,6 +17,8 @@ namespace Umbraco.Core.Models
[EnumMember]
Integer,
[EnumMember]
- Date
+ Date,
+ [EnumMember]
+ Decimal
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/DataTypeDefinition.cs b/src/Umbraco.Core/Models/DataTypeDefinition.cs
index 5d5b9490a5..4c12d6fbef 100644
--- a/src/Umbraco.Core/Models/DataTypeDefinition.cs
+++ b/src/Umbraco.Core/Models/DataTypeDefinition.cs
@@ -49,8 +49,6 @@ namespace Umbraco.Core.Models
_additionalData = new Dictionary();
}
- [Obsolete("Don't use this, parentId is always -1 for data types")]
- [EditorBrowsable(EditorBrowsableState.Never)]
public DataTypeDefinition(int parentId, string propertyEditorAlias)
{
_parentId = parentId;
@@ -267,13 +265,5 @@ namespace Umbraco.Core.Models
{
get { return _additionalData; }
}
-
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- if(Key == default(Guid))
- Key = Guid.NewGuid();
- }
}
}
diff --git a/src/Umbraco.Core/Models/DeepCloneHelper.cs b/src/Umbraco.Core/Models/DeepCloneHelper.cs
index 0e0e260c06..c1b45f63ce 100644
--- a/src/Umbraco.Core/Models/DeepCloneHelper.cs
+++ b/src/Umbraco.Core/Models/DeepCloneHelper.cs
@@ -9,10 +9,30 @@ namespace Umbraco.Core.Models
{
public static class DeepCloneHelper
{
+ ///
+ /// Stores the metadata for the properties for a given type so we know how to create them
+ ///
+ private struct ClonePropertyInfo
+ {
+ public ClonePropertyInfo(PropertyInfo propertyInfo) : this()
+ {
+ if (propertyInfo == null) throw new ArgumentNullException("propertyInfo");
+ PropertyInfo = propertyInfo;
+ }
+
+ public PropertyInfo PropertyInfo { get; private set; }
+ public bool IsDeepCloneable { get; set; }
+ public Type GenericListType { get; set; }
+ public bool IsList
+ {
+ get { return GenericListType != null; }
+ }
+ }
+
///
/// Used to avoid constant reflection (perf)
///
- private static readonly ConcurrentDictionary PropCache = new ConcurrentDictionary();
+ private static readonly ConcurrentDictionary PropCache = new ConcurrentDictionary();
///
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
@@ -30,75 +50,99 @@ namespace Umbraco.Core.Models
throw new InvalidOperationException("Both the input and output types must be the same");
}
+ //get the property metadata from cache so we only have to figure this out once per type
var refProperties = PropCache.GetOrAdd(inputType, type =>
inputType.GetProperties()
- .Where(x =>
- //is not attributed with the ignore clone attribute
- x.GetCustomAttribute() == null
+ .Select(propertyInfo =>
+ {
+ if (
+ //is not attributed with the ignore clone attribute
+ propertyInfo.GetCustomAttribute() != null
//reference type but not string
- && x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
+ || propertyInfo.PropertyType.IsValueType || propertyInfo.PropertyType == typeof (string)
//settable
- && x.CanWrite
+ || propertyInfo.CanWrite == false
//non-indexed
- && x.GetIndexParameters().Any() == false)
+ || propertyInfo.GetIndexParameters().Any())
+ {
+ return null;
+ }
+
+
+ if (TypeHelper.IsTypeAssignableFrom(propertyInfo.PropertyType))
+ {
+ return new ClonePropertyInfo(propertyInfo) { IsDeepCloneable = true };
+ }
+
+ if (TypeHelper.IsTypeAssignableFrom(propertyInfo.PropertyType)
+ && TypeHelper.IsTypeAssignableFrom(propertyInfo.PropertyType) == false)
+ {
+ if (propertyInfo.PropertyType.IsGenericType
+ && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
+ || propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
+ || propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
+ {
+ //if it is a IEnumerable<>, IList or ICollection<> we'll use a List<>
+ var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
+ return new ClonePropertyInfo(propertyInfo) { GenericListType = genericType };
+ }
+ if (propertyInfo.PropertyType.IsArray
+ || (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
+ {
+ //if its an array, we'll create a list to work with first and then convert to array later
+ //otherwise if its just a regular derivitave of IEnumerable, we can use a list too
+ return new ClonePropertyInfo(propertyInfo) { GenericListType = typeof(List) };
+ }
+ //skip instead of trying to create instance of abstract or interface
+ if (propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface)
+ {
+ return null;
+ }
+
+ //its a custom IEnumerable, we'll try to create it
+ try
+ {
+ var custom = Activator.CreateInstance(propertyInfo.PropertyType);
+ //if it's an IList we can work with it, otherwise we cannot
+ var newList = custom as IList;
+ if (newList == null)
+ {
+ return null;
+ }
+ return new ClonePropertyInfo(propertyInfo) {GenericListType = propertyInfo.PropertyType};
+ }
+ catch (Exception)
+ {
+ //could not create this type so we'll skip it
+ return null;
+ }
+ }
+ return new ClonePropertyInfo(propertyInfo);
+ })
+ .Where(x => x.HasValue)
+ .Select(x => x.Value)
.ToArray());
- foreach (var propertyInfo in refProperties)
+ foreach (var clonePropertyInfo in refProperties)
{
- if (TypeHelper.IsTypeAssignableFrom(propertyInfo.PropertyType))
+ if (clonePropertyInfo.IsDeepCloneable)
{
//this ref property is also deep cloneable so clone it
- var result = (IDeepCloneable)propertyInfo.GetValue(input, null);
+ var result = (IDeepCloneable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
if (result != null)
{
//set the cloned value to the property
- propertyInfo.SetValue(output, result.DeepClone(), null);
+ clonePropertyInfo.PropertyInfo.SetValue(output, result.DeepClone(), null);
}
}
- else if (TypeHelper.IsTypeAssignableFrom(propertyInfo.PropertyType)
- && TypeHelper.IsTypeAssignableFrom(propertyInfo.PropertyType) == false)
+ else if (clonePropertyInfo.IsList)
{
- IList newList;
- if (propertyInfo.PropertyType.IsGenericType
- && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
- || propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
- || propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
- {
- //if it is a IEnumerable<>, IList or ICollection<> we'll use a List<>
- var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
- newList = (IList)Activator.CreateInstance(genericType);
- }
- else if (propertyInfo.PropertyType.IsArray
- || (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
- {
- //if its an array, we'll create a list to work with first and then convert to array later
- //otherwise if its just a regular derivitave of IEnumerable, we can use a list too
- newList = new List();
- }
- else
- {
- //its a custom IEnumerable, we'll try to create it
- try
- {
- var custom = Activator.CreateInstance(propertyInfo.PropertyType);
- //if it's an IList we can work with it, otherwise we cannot
- newList = custom as IList;
- if (newList == null)
- {
- continue;
- }
- }
- catch (Exception)
- {
- //could not create this type so we'll skip it
- continue;
- }
- }
-
- var enumerable = (IEnumerable)propertyInfo.GetValue(input, null);
+ var enumerable = (IEnumerable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
if (enumerable == null) continue;
+ var newList = (IList)Activator.CreateInstance(clonePropertyInfo.GenericListType);
+
var isUsableType = true;
//now clone each item
@@ -130,21 +174,21 @@ namespace Umbraco.Core.Models
continue;
}
- if (propertyInfo.PropertyType.IsArray)
+ if (clonePropertyInfo.PropertyInfo.PropertyType.IsArray)
{
//need to convert to array
- var arr = (object[])Activator.CreateInstance(propertyInfo.PropertyType, newList.Count);
+ var arr = (object[])Activator.CreateInstance(clonePropertyInfo.PropertyInfo.PropertyType, newList.Count);
for (int i = 0; i < newList.Count; i++)
{
arr[i] = newList[i];
}
//set the cloned collection
- propertyInfo.SetValue(output, arr, null);
+ clonePropertyInfo.PropertyInfo.SetValue(output, arr, null);
}
else
{
//set the cloned collection
- propertyInfo.SetValue(output, newList, null);
+ clonePropertyInfo.PropertyInfo.SetValue(output, newList, null);
}
}
diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs
index 7e59726c80..749c629d19 100644
--- a/src/Umbraco.Core/Models/DictionaryItem.cs
+++ b/src/Umbraco.Core/Models/DictionaryItem.cs
@@ -14,6 +14,7 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class DictionaryItem : Entity, IDictionaryItem
{
+ public Func GetLanguage { get; set; }
private Guid? _parentId;
private string _itemKey;
private IEnumerable _translations;
@@ -78,7 +79,17 @@ namespace Umbraco.Core.Models
{
SetPropertyValueAndDetectChanges(o =>
{
- _translations = value;
+ var asArray = value.ToArray();
+ //ensure the language callback is set on each translation
+ if (GetLanguage != null)
+ {
+ foreach (var translation in asArray.OfType())
+ {
+ translation.GetLanguage = GetLanguage;
+ }
+ }
+
+ _translations = asArray;
return _translations;
}, _translations, TranslationsSelector,
//Custom comparer for enumerable
@@ -87,16 +98,5 @@ namespace Umbraco.Core.Models
enumerable => enumerable.GetHashCode()));
}
}
-
- ///
- /// Method to call before inserting a new entity in the db
- ///
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- Key = Guid.NewGuid();
- }
-
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/DictionaryTranslation.cs b/src/Umbraco.Core/Models/DictionaryTranslation.cs
index 782ff14413..59f96dbe85 100644
--- a/src/Umbraco.Core/Models/DictionaryTranslation.cs
+++ b/src/Umbraco.Core/Models/DictionaryTranslation.cs
@@ -13,13 +13,18 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class DictionaryTranslation : Entity, IDictionaryTranslation
{
+ internal Func GetLanguage { get; set; }
+
private ILanguage _language;
private string _value;
+ //note: this will be memberwise cloned
+ private int _languageId;
public DictionaryTranslation(ILanguage language, string value)
{
if (language == null) throw new ArgumentNullException("language");
_language = language;
+ _languageId = _language.Id;
_value = value;
}
@@ -27,6 +32,20 @@ namespace Umbraco.Core.Models
{
if (language == null) throw new ArgumentNullException("language");
_language = language;
+ _languageId = _language.Id;
+ _value = value;
+ Key = uniqueId;
+ }
+
+ internal DictionaryTranslation(int languageId, string value)
+ {
+ _languageId = languageId;
+ _value = value;
+ }
+
+ internal DictionaryTranslation(int languageId, string value, Guid uniqueId)
+ {
+ _languageId = languageId;
_value = value;
Key = uniqueId;
}
@@ -37,20 +56,43 @@ namespace Umbraco.Core.Models
///
/// Gets or sets the for the translation
///
+ ///
+ /// Marked as DoNotClone - TODO: this member shouldn't really exist here in the first place, the DictionaryItem
+ /// class will have a deep hierarchy of objects which all get deep cloned which we don't want. This should have simply
+ /// just referenced a language ID not the actual language object. In v8 we need to fix this.
+ /// We're going to have to do the same hacky stuff we had to do with the Template/File contents so that this is returned
+ /// on a callback.
+ ///
[DataMember]
+ [DoNotClone]
public ILanguage Language
{
- get { return _language; }
+ get
+ {
+ if (_language != null)
+ return _language;
+
+ // else, must lazy-load
+ if (GetLanguage != null && _languageId > 0)
+ _language = GetLanguage(_languageId);
+ return _language;
+ }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_language = value;
+ _languageId = _language == null ? -1 : _language.Id;
return _language;
}, _language, LanguageSelector);
}
}
+ public int LanguageId
+ {
+ get { return _languageId; }
+ }
+
///
/// Gets or sets the translated text
///
@@ -68,5 +110,23 @@ namespace Umbraco.Core.Models
}
}
+ public override object DeepClone()
+ {
+ var clone = (DictionaryTranslation)base.DeepClone();
+
+ // clear fields that were memberwise-cloned and that we don't want to clone
+ clone._language = null;
+
+ // turn off change tracking
+ clone.DisableChangeTracking();
+
+ // this shouldn't really be needed since we're not tracking
+ clone.ResetDirtyProperties(false);
+
+ // re-enable tracking
+ clone.EnableChangeTracking();
+
+ return clone;
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs
index eeacb771a9..c4838dfd0a 100644
--- a/src/Umbraco.Core/Models/EntityBase/Entity.cs
+++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs
@@ -62,9 +62,9 @@ namespace Umbraco.Core.Models.EntityBase
{
get
{
+ // if an entity does NOT have a UniqueId yet, assign one now
if (_key == Guid.Empty)
- return _id.ToGuid();
-
+ _key = Guid.NewGuid();
return _key;
}
set
@@ -136,6 +136,7 @@ namespace Umbraco.Core.Models.EntityBase
{
_hasIdentity = false;
_id = default(int);
+ _key = Guid.Empty;
}
///
@@ -242,6 +243,7 @@ namespace Umbraco.Core.Models.EntityBase
{
//Memberwise clone on Entity will work since it doesn't have any deep elements
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
+ var ignored = this.Key; // ensure that 'this' has a key, before cloning
var clone = (Entity)MemberwiseClone();
//ensure the clone has it's own dictionaries
clone.ResetChangeTrackingCollections();
diff --git a/src/Umbraco.Core/Models/EntityContainer.cs b/src/Umbraco.Core/Models/EntityContainer.cs
new file mode 100644
index 0000000000..92965bf238
--- /dev/null
+++ b/src/Umbraco.Core/Models/EntityContainer.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Represents a folder for organizing entities such as content types and data types.
+ ///
+ public sealed class EntityContainer : UmbracoEntity
+ {
+ private readonly Guid _containedObjectType;
+
+ private static readonly Dictionary ObjectTypeMap = new Dictionary
+ {
+ { Constants.ObjectTypes.DataTypeGuid, Constants.ObjectTypes.DataTypeContainerGuid },
+ { Constants.ObjectTypes.DocumentTypeGuid, Constants.ObjectTypes.DocumentTypeContainerGuid },
+ { Constants.ObjectTypes.MediaTypeGuid, Constants.ObjectTypes.MediaTypeContainerGuid }
+ };
+
+ ///
+ /// Initializes a new instance of an class.
+ ///
+ public EntityContainer(Guid containedObjectType)
+ {
+ if (ObjectTypeMap.ContainsKey(containedObjectType) == false)
+ throw new ArgumentException("Not a contained object type.", "containedObjectType");
+ _containedObjectType = containedObjectType;
+
+ ParentId = -1;
+ Path = "-1";
+ Level = 0;
+ SortOrder = 0;
+ }
+
+ ///
+ /// Initializes a new instance of an class.
+ ///
+ internal EntityContainer(int id, Guid uniqueId, int parentId, string path, int level, int sortOrder, Guid containedObjectType, string name, int userId)
+ : this(containedObjectType)
+ {
+ Id = id;
+ Key = uniqueId;
+ ParentId = parentId;
+ Name = name;
+ Path = path;
+ Level = level;
+ SortOrder = sortOrder;
+ CreatorId = userId;
+ }
+
+ ///
+ /// Gets or sets the node object type of the contained objects.
+ ///
+ public Guid ContainedObjectType
+ {
+ get { return _containedObjectType; }
+ }
+
+ ///
+ /// Gets the node object type of the container objects.
+ ///
+ public Guid ContainerObjectType
+ {
+ get { return ObjectTypeMap[_containedObjectType]; }
+ }
+
+ ///
+ /// Gets the container object type corresponding to a contained object type.
+ ///
+ /// The contained object type.
+ /// The object type of containers containing objects of the contained object type.
+ public static Guid GetContainerObjectType(Guid containedObjectType)
+ {
+ if (ObjectTypeMap.ContainsKey(containedObjectType) == false)
+ throw new ArgumentException("Not a contained object type.", "containedObjectType");
+ return ObjectTypeMap[containedObjectType];
+ }
+
+ ///
+ /// Gets the contained object type corresponding to a container object type.
+ ///
+ /// The container object type.
+ /// The object type of objects that containers of the container object type can contain.
+ public static Guid GetContainedObjectType(Guid containerObjectType)
+ {
+ var contained = ObjectTypeMap.FirstOrDefault(x => x.Value == containerObjectType).Key;
+ if (contained == null)
+ throw new ArgumentException("Not a container object type.", "containerObjectType");
+ return contained;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs
index 2715e5fe3a..7caefc1121 100644
--- a/src/Umbraco.Core/Models/IContent.cs
+++ b/src/Umbraco.Core/Models/IContent.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Diagnostics;
using Umbraco.Core.Persistence.Mappers;
@@ -21,6 +22,7 @@ namespace Umbraco.Core.Models
bool Published { get; }
[Obsolete("This will be removed in future versions")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
string Language { get; set; }
///
diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs
index 89fca20b8e..80e62f50cf 100644
--- a/src/Umbraco.Core/Models/IContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/IContentTypeBase.cs
@@ -54,10 +54,15 @@ namespace Umbraco.Core.Models
PropertyGroupCollection PropertyGroups { get; set; }
///
- /// Gets an enumerable list of Property Types aggregated for all groups
+ /// Gets all property types, across all property groups.
///
IEnumerable PropertyTypes { get; }
+ ///
+ /// Gets or sets the property types that are not in a group.
+ ///
+ IEnumerable NoGroupPropertyTypes { get; set; }
+
///
/// Removes a PropertyType from the current ContentType
///
diff --git a/src/Umbraco.Core/Models/IContentTypeComposition.cs b/src/Umbraco.Core/Models/IContentTypeComposition.cs
index 8b5049f236..ab7e068fdd 100644
--- a/src/Umbraco.Core/Models/IContentTypeComposition.cs
+++ b/src/Umbraco.Core/Models/IContentTypeComposition.cs
@@ -8,17 +8,17 @@ namespace Umbraco.Core.Models
public interface IContentTypeComposition : IContentTypeBase
{
///
- /// Gets a list of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType
+ /// Gets or sets the content types that compose this content type.
///
- IEnumerable ContentTypeComposition { get; }
+ IEnumerable ContentTypeComposition { get; set; }
///
- /// Gets a list of objects from the composition
+ /// Gets the property groups for the entire composition.
///
IEnumerable CompositionPropertyGroups { get; }
///
- /// Gets a list of objects from the composition
+ /// Gets the property types for the entire composition.
///
IEnumerable CompositionPropertyTypes { get; }
diff --git a/src/Umbraco.Core/Models/IDictionaryTranslation.cs b/src/Umbraco.Core/Models/IDictionaryTranslation.cs
index cf813bf72f..25aa1e4395 100644
--- a/src/Umbraco.Core/Models/IDictionaryTranslation.cs
+++ b/src/Umbraco.Core/Models/IDictionaryTranslation.cs
@@ -12,6 +12,8 @@ namespace Umbraco.Core.Models
[DataMember]
ILanguage Language { get; set; }
+ int LanguageId { get; }
+
///
/// Gets or sets the translated text
///
diff --git a/src/Umbraco.Core/Models/IMediaType.cs b/src/Umbraco.Core/Models/IMediaType.cs
index 3934d7a40f..29e4b665ba 100644
--- a/src/Umbraco.Core/Models/IMediaType.cs
+++ b/src/Umbraco.Core/Models/IMediaType.cs
@@ -7,6 +7,12 @@ namespace Umbraco.Core.Models
///
+ /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
+ ///
+ ///
+ ///
+ IMediaType DeepCloneWithResetIdentities(string newAlias);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
index f57d6683a2..0dc95a8987 100644
--- a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
+++ b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
@@ -4,6 +4,7 @@ using AutoMapper;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Security;
namespace Umbraco.Core.Models.Identity
{
@@ -24,6 +25,18 @@ namespace Umbraco.Core.Models.Identity
.ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias))
.ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts))
.ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray()));
+
+ config.CreateMap()
+ .ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
+ .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
+ .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
+ .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
+ .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias }))
+ .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId))
+ .ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId))
+ .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName))
+ .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture))
+ .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp));
}
private string GetPasswordHash(string storedPass)
diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs
index ef8ebbf931..b23bbfb52a 100644
--- a/src/Umbraco.Core/Models/Language.cs
+++ b/src/Umbraco.Core/Models/Language.cs
@@ -64,7 +64,7 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
public CultureInfo CultureInfo
{
- get { return CultureInfo.CreateSpecificCulture(IsoCode); }
+ get { return CultureInfo.GetCultureInfo(IsoCode); }
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Mapping/MappingExpressionExtensions.cs b/src/Umbraco.Core/Models/Mapping/MappingExpressionExtensions.cs
new file mode 100644
index 0000000000..570e51dbc3
--- /dev/null
+++ b/src/Umbraco.Core/Models/Mapping/MappingExpressionExtensions.cs
@@ -0,0 +1,20 @@
+using AutoMapper;
+
+namespace Umbraco.Core.Models.Mapping
+{
+ internal static class MappingExpressionExtensions
+ {
+ ///
+ /// Ignores all unmapped members by default - Use with caution!
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IMappingExpression IgnoreAllUnmapped(this IMappingExpression expression)
+ {
+ expression.ForAllMembers(opt => opt.Ignore());
+ return expression;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Media.cs b/src/Umbraco.Core/Models/Media.cs
index 4f922d28cf..a7e794a400 100644
--- a/src/Umbraco.Core/Models/Media.cs
+++ b/src/Umbraco.Core/Models/Media.cs
@@ -118,17 +118,5 @@ namespace Umbraco.Core.Models
//The Media Recycle Bin Id is -21 so we correct that here
ParentId = parentId == -20 ? -21 : parentId;
}
-
- ///
- /// Method to call when Entity is being saved
- ///
- /// Created date is set and a Unique key is assigned
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- if (Key == Guid.Empty)
- Key = Guid.NewGuid();
- }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/MediaExtensions.cs b/src/Umbraco.Core/Models/MediaExtensions.cs
new file mode 100644
index 0000000000..1f2e1b62b2
--- /dev/null
+++ b/src/Umbraco.Core/Models/MediaExtensions.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.Logging;
+using Umbraco.Core.PropertyEditors.ValueConverters;
+
+namespace Umbraco.Core.Models
+{
+ internal static class MediaExtensions
+ {
+ ///
+ /// Hack: we need to put this in a real place, this is currently just used to render the urls for a media item in the back office
+ ///
+ ///
+ public static string GetUrl(this IMedia media, string propertyAlias, ILogger logger)
+ {
+ var propertyType = media.PropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyAlias));
+ if (propertyType != null)
+ {
+ var val = media.Properties[propertyType];
+ if (val != null)
+ {
+ var jsonString = val.Value as string;
+ if (jsonString != null)
+ {
+ if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias)
+ {
+ if (jsonString.DetectIsJson())
+ {
+ try
+ {
+ var json = JsonConvert.DeserializeObject(jsonString);
+ if (json["src"] != null)
+ {
+ return json["src"].Value();
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.Error("Could not parse the string " + jsonString + " to a json object", ex);
+ return string.Empty;
+ }
+ }
+ else
+ {
+ return jsonString;
+ }
+ }
+ else if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias)
+ {
+ return jsonString;
+ }
+ //hrm, without knowing what it is, just adding a string here might not be very nice
+ }
+ }
+ }
+ return string.Empty;
+ }
+
+ ///
+ /// Hack: we need to put this in a real place, this is currently just used to render the urls for a media item in the back office
+ ///
+ ///
+ public static string[] GetUrls(this IMedia media, IContentSection contentSection, ILogger logger)
+ {
+ var links = new List();
+ var autoFillProperties = contentSection.ImageAutoFillProperties.ToArray();
+ if (autoFillProperties.Any())
+ {
+ links.AddRange(
+ autoFillProperties
+ .Select(field => media.GetUrl(field.Alias, logger))
+ .Where(link => link.IsNullOrWhiteSpace() == false));
+ }
+ return links.ToArray();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs
index 052e231136..8596cce910 100644
--- a/src/Umbraco.Core/Models/MediaType.cs
+++ b/src/Umbraco.Core/Models/MediaType.cs
@@ -40,24 +40,28 @@ namespace Umbraco.Core.Models
}
///
- /// Method to call when Entity is being saved
+ /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
///
- /// Created date is set and a Unique key is assigned
- internal override void AddingEntity()
+ ///
+ public IMediaType DeepCloneWithResetIdentities(string alias)
{
- base.AddingEntity();
+ var clone = (MediaType)DeepClone();
+ clone.Alias = alias;
+ clone.Key = Guid.Empty;
+ foreach (var propertyGroup in clone.PropertyGroups)
+ {
+ propertyGroup.ResetIdentity();
+ propertyGroup.ResetDirtyProperties(false);
+ }
+ foreach (var propertyType in clone.PropertyTypes)
+ {
+ propertyType.ResetIdentity();
+ propertyType.ResetDirtyProperties(false);
+ }
- if (Key == Guid.Empty)
- Key = Guid.NewGuid();
- }
-
- ///
- /// Method to call when Entity is being updated
- ///
- /// Modified Date is set and a new Version guid is set
- internal override void UpdatingEntity()
- {
- base.UpdatingEntity();
+ clone.ResetIdentity();
+ clone.ResetDirtyProperties(false);
+ return clone;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs
index d416c846ef..70c21e4307 100644
--- a/src/Umbraco.Core/Models/Member.cs
+++ b/src/Umbraco.Core/Models/Member.cs
@@ -280,7 +280,7 @@ namespace Umbraco.Core.Models
//This is the default value if the prop is not found
true);
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.IsApproved].Value == null) return true;
var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -313,7 +313,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, "IsLockedOut", false);
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null) return false;
var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -346,7 +346,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, "LastLoginDate", default(DateTime));
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -379,7 +379,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, "LastPasswordChangeDate", default(DateTime));
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -412,7 +412,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, "LastLockoutDate", default(DateTime));
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -446,7 +446,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, "FailedPasswordAttempts", 0);
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null) return default(int);
var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -509,11 +509,8 @@ namespace Umbraco.Core.Models
{
base.AddingEntity();
- if (Key == Guid.Empty)
- {
- Key = Guid.NewGuid();
+ if (ProviderUserKey == null)
ProviderUserKey = Key;
- }
}
///
diff --git a/src/Umbraco.Core/Models/MemberGroup.cs b/src/Umbraco.Core/Models/MemberGroup.cs
index e52448a11d..ff7e05be9e 100644
--- a/src/Umbraco.Core/Models/MemberGroup.cs
+++ b/src/Umbraco.Core/Models/MemberGroup.cs
@@ -60,17 +60,5 @@ namespace Umbraco.Core.Models
}
public IDictionary AdditionalData { get; private set; }
-
- ///
- /// Method to call when Entity is being saved
- ///
- /// Created date is set and a Unique key is assigned
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- if (Key == Guid.Empty)
- Key = Guid.NewGuid();
- }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs
index 74a879be81..9000a33b54 100644
--- a/src/Umbraco.Core/Models/MemberType.cs
+++ b/src/Umbraco.Core/Models/MemberType.cs
@@ -131,26 +131,5 @@ namespace Umbraco.Core.Models
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
}
}
-
- ///
- /// Method to call when Entity is being saved
- ///
- /// Created date is set and a Unique key is assigned
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- if (Key == Guid.Empty)
- Key = Guid.NewGuid();
- }
-
- ///
- /// Method to call when Entity is being updated
- ///
- /// Modified Date is set and a new Version guid is set
- internal override void UpdatingEntity()
- {
- base.UpdatingEntity();
- }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index d7c2eb92a8..f3fbefda4f 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -124,10 +124,54 @@ namespace Umbraco.Core.Models
bool typeValidation = _propertyType.IsPropertyTypeValid(value);
if (typeValidation == false)
- throw new Exception(
- string.Format(
- "Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'",
- value == null ? "null" : value.GetType().Name, Alias));
+ {
+ // Normally we'll throw an exception here. However if the property is of a type that can have it's data field (dataInt, dataVarchar etc.)
+ // changed, we might have a value of the now "wrong" type. As of May 2016 Label is the only built-in property editor that supports this.
+ // In that case we should try to parse the value and return null if that's not possible rather than throwing an exception.
+ if (value != null && _propertyType.CanHaveDataValueTypeChanged())
+ {
+ var stringValue = value.ToString();
+ switch (_propertyType.DataTypeDatabaseType)
+ {
+ case DataTypeDatabaseType.Nvarchar:
+ case DataTypeDatabaseType.Ntext:
+ value = stringValue;
+ break;
+ case DataTypeDatabaseType.Integer:
+ int integerValue;
+ if (int.TryParse(stringValue, out integerValue) == false)
+ {
+ // Edge case, but if changed from decimal --> integer, the above tryparse will fail. So we'll try going
+ // via decimal too to return the integer value rather than zero.
+ decimal decimalForIntegerValue;
+ if (decimal.TryParse(stringValue, out decimalForIntegerValue))
+ {
+ integerValue = (int)decimalForIntegerValue;
+ }
+ }
+
+ value = integerValue;
+ break;
+ case DataTypeDatabaseType.Decimal:
+ decimal decimalValue;
+ decimal.TryParse(stringValue, out decimalValue);
+ value = decimalValue;
+ break;
+ case DataTypeDatabaseType.Date:
+ DateTime dateValue;
+ DateTime.TryParse(stringValue, out dateValue);
+ value = dateValue;
+ break;
+ }
+ }
+ else
+ {
+ throw new Exception(
+ string.Format(
+ "Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'",
+ value == null ? "null" : value.GetType().Name, Alias));
+ }
+ }
SetPropertyValueAndDetectChanges(o =>
{
diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs
index 7976a7eac6..de88012c0e 100644
--- a/src/Umbraco.Core/Models/PropertyGroup.cs
+++ b/src/Umbraco.Core/Models/PropertyGroup.cs
@@ -17,7 +17,6 @@ namespace Umbraco.Core.Models
public class PropertyGroup : Entity, IEquatable
{
private string _name;
- private Lazy _parentId;
private int _sortOrder;
private PropertyTypeCollection _propertyTypes;
@@ -31,7 +30,6 @@ namespace Umbraco.Core.Models
}
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
- private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId);
private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder);
private readonly static PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes);
void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e)
@@ -56,29 +54,6 @@ namespace Umbraco.Core.Models
}
}
- ///
- /// Gets or sets the Id of the Parent PropertyGroup.
- ///
- ///
- /// A Parent PropertyGroup corresponds to an inherited PropertyGroup from a composition.
- /// If a PropertyType is inserted into an inherited group then a new group will be created with an Id reference to the parent.
- ///
- [DataMember]
- public int? ParentId
- {
- get
- {
- if (_parentId == null)
- return default(int?);
- return _parentId.Value;
- }
- set
- {
- _parentId = new Lazy(() => value);
- OnPropertyChanged(ParentIdSelector);
- }
- }
-
///
/// Gets or sets the Sort Order of the Group
///
@@ -117,15 +92,6 @@ namespace Umbraco.Core.Models
}
}
- ///
- /// Sets the ParentId from the lazy integer id
- ///
- /// Id of the Parent
- internal void SetLazyParentId(Lazy id)
- {
- _parentId = id;
- }
-
public bool Equals(PropertyGroup other)
{
if (base.Equals(other)) return true;
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index e9d4451314..846b907197 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
+using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
@@ -9,6 +10,7 @@ using Umbraco.Core.Strings;
namespace Umbraco.Core.Models
{
+
///
/// Defines the type of a object
///
@@ -32,6 +34,8 @@ namespace Umbraco.Core.Models
public PropertyType(IDataTypeDefinition dataTypeDefinition)
{
+ if (dataTypeDefinition == null) throw new ArgumentNullException("dataTypeDefinition");
+
if(dataTypeDefinition.HasIdentity)
_dataTypeDefinitionId = dataTypeDefinition.Id;
@@ -218,8 +222,9 @@ namespace Umbraco.Core.Models
}
///
- /// Gets or Sets the PropertyGroup's Id for which this PropertyType belongs
+ /// Gets or sets the identifier of the PropertyGroup this PropertyType belongs to.
///
+ /// For generic properties, the value is null .
[DataMember]
internal Lazy PropertyGroupId
{
@@ -401,6 +406,9 @@ namespace Umbraco.Core.Models
if (DataTypeDatabaseType == DataTypeDatabaseType.Integer && type == typeof(int))
return true;
+ if (DataTypeDatabaseType == DataTypeDatabaseType.Decimal && type == typeof(decimal))
+ return true;
+
if (DataTypeDatabaseType == DataTypeDatabaseType.Date && type == typeof(DateTime))
return true;
@@ -418,6 +426,19 @@ namespace Umbraco.Core.Models
return false;
}
+ ///
+ /// Checks the underlying property editor prevalues to see if the one that allows changing of the database field
+ /// to which data is saved (dataInt, dataVarchar etc.) is included. If so that means the field could be changed when the data
+ /// type is saved.
+ ///
+ ///
+ internal bool CanHaveDataValueTypeChanged()
+ {
+ var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias);
+ return propertyEditor.PreValueEditor.Fields
+ .SingleOrDefault(x => x.Key == Constants.PropertyEditors.PreValueKeys.DataValueType) != null;
+ }
+
///
/// Validates the Value from a Property according to the validation settings
///
diff --git a/src/Umbraco.Core/Models/PublicAccessEntry.cs b/src/Umbraco.Core/Models/PublicAccessEntry.cs
index 27d1cd2121..9e12d6ab57 100644
--- a/src/Umbraco.Core/Models/PublicAccessEntry.cs
+++ b/src/Umbraco.Core/Models/PublicAccessEntry.cs
@@ -108,18 +108,6 @@ namespace Umbraco.Core.Models
}
}
- ///
- /// Method to call on entity saved when first added
- ///
- internal override void AddingEntity()
- {
- if (Key == default(Guid))
- {
- Key = Guid.NewGuid();
- }
- base.AddingEntity();
- }
-
[DataMember]
public int LoginNodeId
{
diff --git a/src/Umbraco.Core/Models/PublicAccessRule.cs b/src/Umbraco.Core/Models/PublicAccessRule.cs
index 484652e8bd..c785d028d0 100644
--- a/src/Umbraco.Core/Models/PublicAccessRule.cs
+++ b/src/Umbraco.Core/Models/PublicAccessRule.cs
@@ -28,18 +28,6 @@ namespace Umbraco.Core.Models
public Guid AccessEntryId { get; internal set; }
- ///
- /// Method to call on entity saved when first added
- ///
- internal override void AddingEntity()
- {
- if (Key == default(Guid))
- {
- Key = Guid.NewGuid();
- }
- base.AddingEntity();
- }
-
public string RuleValue
{
get { return _ruleValue; }
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
index 5f30c08ce7..d05960b08f 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
@@ -1,9 +1,6 @@
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
-using System.Web.Caching;
-using System.Web.UI;
using Umbraco.Core.Cache;
namespace Umbraco.Core.Models.PublishedContent
@@ -16,6 +13,7 @@ namespace Umbraco.Core.Models.PublishedContent
public class PublishedContentType
{
private readonly PublishedPropertyType[] _propertyTypes;
+ private readonly HashSet _compositionAliases;
// fast alias-to-index xref containing both the raw alias and its lowercase version
private readonly Dictionary _indexes = new Dictionary();
@@ -27,6 +25,7 @@ namespace Umbraco.Core.Models.PublishedContent
{
Id = contentType.Id;
Alias = contentType.Alias;
+ _compositionAliases = new HashSet(contentType.CompositionAliases(), StringComparer.InvariantCultureIgnoreCase);
_propertyTypes = contentType.CompositionPropertyTypes
.Select(x => new PublishedPropertyType(this, x))
.ToArray();
@@ -34,10 +33,11 @@ namespace Umbraco.Core.Models.PublishedContent
}
// internal so it can be used for unit tests
- internal PublishedContentType(int id, string alias, IEnumerable propertyTypes)
+ internal PublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes)
{
Id = id;
Alias = alias;
+ _compositionAliases = new HashSet(compositionAliases, StringComparer.InvariantCultureIgnoreCase);
_propertyTypes = propertyTypes.ToArray();
foreach (var propertyType in _propertyTypes)
propertyType.ContentType = this;
@@ -45,8 +45,8 @@ namespace Umbraco.Core.Models.PublishedContent
}
// create detached content type - ie does not match anything in the DB
- internal PublishedContentType(string alias, IEnumerable propertyTypes)
- : this (0, alias, propertyTypes)
+ internal PublishedContentType(string alias, IEnumerable compositionAliases, IEnumerable propertyTypes)
+ : this(0, alias, compositionAliases, propertyTypes)
{ }
private void InitializeIndexes()
@@ -63,6 +63,7 @@ namespace Umbraco.Core.Models.PublishedContent
public int Id { get; private set; }
public string Alias { get; private set; }
+ public HashSet CompositionAliases { get { return _compositionAliases; } }
#endregion
@@ -113,10 +114,25 @@ namespace Umbraco.Core.Models.PublishedContent
internal static void ClearContentType(int id)
{
Logging.LogHelper.Debug("Clear content type w/id {0}.", () => id);
- // requires a predicate because the key does not contain the ID
- // faster than key strings comparisons anyway
+
+ // we don't support "get all" at the moment - so, cheating
+ var all = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItemsByKeySearch("PublishedContentType_").ToArray();
+
+ // the one we want to clear
+ var clr = all.FirstOrDefault(x => x.Id == id);
+ if (clr == null) return;
+
+ // those that have that one in their composition aliases
+ // note: CompositionAliases contains all recursive aliases
+ var oth = all.Where(x => x.CompositionAliases.InvariantContains(clr.Alias)).Select(x => x.Id);
+
+ // merge ids
+ var ids = oth.Concat(new[] { clr.Id }).ToArray();
+
+ // clear them all at once
+ // we don't support "clear many at once" at the moment - so, cheating
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes(
- (key, value) => value.Id == id);
+ (key, value) => ids.Contains(value.Id));
}
internal static void ClearDataType(int id)
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
index f4b1597a7d..1875ae03b3 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
@@ -164,6 +164,9 @@ namespace Umbraco.Core.Models.PublishedContent
private void InitializeConverters()
{
+ //TODO: Look at optimizing this method, it gets run for every property type for the document being rendered at startup,
+ // every precious second counts!
+
var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
var defaultConvertersWithAttributes = PropertyValueConvertersResolver.Current.DefaultConverters;
@@ -230,13 +233,13 @@ namespace Umbraco.Core.Models.PublishedContent
{
_sourceCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Source);
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Object);
- _objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
+ _xpathCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
}
else
{
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
- _objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
+ _xpathCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
}
if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel;
if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel;
diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs
similarity index 93%
rename from src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs
rename to src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs
index e3fdfb9810..88ef02ea90 100644
--- a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs
@@ -1,28 +1,28 @@
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Persistence.DatabaseAnnotations;
-
-namespace Umbraco.Core.Models.Rdbms
-{
- [TableName("cmsDocumentType")]
- [PrimaryKey("contentTypeNodeId", autoIncrement = false)]
- [ExplicitColumns]
- internal class DocumentTypeDto
- {
- [Column("contentTypeNodeId")]
- [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsDocumentType", OnColumns = "contentTypeNodeId, templateNodeId")]
- [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")]
- [ForeignKey(typeof(NodeDto))]
- public int ContentTypeNodeId { get; set; }
-
- [Column("templateNodeId")]
- [ForeignKey(typeof(TemplateDto), Column = "nodeId")]
- public int TemplateNodeId { get; set; }
-
- [Column("IsDefault")]
- [Constraint(Default = "0")]
- public bool IsDefault { get; set; }
-
- [ResultColumn]
- public ContentTypeDto ContentTypeDto { get; set; }
- }
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("cmsDocumentType")]
+ [PrimaryKey("contentTypeNodeId", autoIncrement = false)]
+ [ExplicitColumns]
+ internal class ContentTypeTemplateDto
+ {
+ [Column("contentTypeNodeId")]
+ [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsDocumentType", OnColumns = "contentTypeNodeId, templateNodeId")]
+ [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")]
+ [ForeignKey(typeof(NodeDto))]
+ public int ContentTypeNodeId { get; set; }
+
+ [Column("templateNodeId")]
+ [ForeignKey(typeof(TemplateDto), Column = "nodeId")]
+ public int TemplateNodeId { get; set; }
+
+ [Column("IsDefault")]
+ [Constraint(Default = "0")]
+ public bool IsDefault { get; set; }
+
+ [ResultColumn]
+ public ContentTypeDto ContentTypeDto { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs b/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs
index 5ab339fe01..1a76895e90 100644
--- a/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models.Rdbms
internal class MigrationDto
{
[Column("id")]
- [PrimaryKeyColumn(AutoIncrement = true)]
+ [PrimaryKeyColumn(AutoIncrement = true, IdentitySeed = 100)]
public int Id { get; set; }
[Column("name")]
diff --git a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
index 7003c58e77..c5fac092df 100644
--- a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
@@ -10,12 +10,6 @@ namespace Umbraco.Core.Models.Rdbms
[ExplicitColumns]
internal class NodeDto
{
- public NodeDto()
- {
- //By default, always generate a new guid
- UniqueId = Guid.NewGuid();
- }
-
public const int NodeIdSeed = 1050;
[Column("id")]
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
index 4e59f0275b..63e3104d12 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
@@ -33,6 +33,10 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.Null)]
public int? Integer { get; set; }
+ [Column("dataDecimal")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public decimal? Decimal { get; set; }
+
[Column("dataDate")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? Date { get; set; }
@@ -55,22 +59,27 @@ namespace Umbraco.Core.Models.Rdbms
{
get
{
- if(Integer.HasValue)
+ if (Integer.HasValue)
{
return Integer.Value;
}
+
+ if (Decimal.HasValue)
+ {
+ return Decimal.Value;
+ }
- if(Date.HasValue)
+ if (Date.HasValue)
{
return Date.Value;
}
- if(string.IsNullOrEmpty(VarChar) == false)
+ if (string.IsNullOrEmpty(VarChar) == false)
{
return VarChar;
}
- if(string.IsNullOrEmpty(Text) == false)
+ if (string.IsNullOrEmpty(Text) == false)
{
return Text;
}
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs
index 74a6d34289..2be4b24157 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs
@@ -10,12 +10,6 @@ namespace Umbraco.Core.Models.Rdbms
[ExplicitColumns]
internal class PropertyTypeDto
{
- public PropertyTypeDto()
- {
- //by default always create a new guid
- UniqueId = Guid.NewGuid();
- }
-
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 50)]
public int Id { get; set; }
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs
index 51cb9872fe..68de420a97 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs
@@ -1,6 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -13,12 +15,6 @@ namespace Umbraco.Core.Models.Rdbms
[PrimaryKeyColumn(IdentitySeed = 12)]
public int Id { get; set; }
- [Column("parentGroupId")]
- [NullSetting(NullSetting = NullSettings.Null)]
- //[Constraint(Default = "NULL")]
- [ForeignKey(typeof(PropertyTypeGroupDto))]
- public int? ParentGroupId { get; set; }
-
[Column("contenttypeNodeId")]
[ForeignKey(typeof(ContentTypeDto), Column = "nodeId")]
public int ContentTypeNodeId { get; set; }
@@ -31,5 +27,11 @@ namespace Umbraco.Core.Models.Rdbms
[ResultColumn]
public List PropertyTypeDtos { get; set; }
+
+ [Column("uniqueID")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Constraint(Default = SystemMethods.NewGuid)]
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeGroupUniqueID")]
+ public Guid UniqueId { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs
index 8dcc4af29c..57c973fb64 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs
@@ -1,4 +1,5 @@
-using Umbraco.Core.Persistence;
+using System;
+using Umbraco.Core.Persistence;
namespace Umbraco.Core.Models.Rdbms
{
@@ -10,9 +11,6 @@ namespace Umbraco.Core.Models.Rdbms
[Column("PropertyTypeGroupId")]
public int? Id { get; set; }
- [Column("parentGroupId")]
- public int? ParentGroupId { get; set; }
-
[Column("PropertyGroupName")]
public string Text { get; set; }
@@ -21,5 +19,8 @@ namespace Umbraco.Core.Models.Rdbms
[Column("contenttypeNodeId")]
public int ContentTypeNodeId { get; set; }
+
+ [Column("PropertyGroupUniqueID")]
+ public Guid UniqueId { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs
index 2f448860b0..d85baa2884 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs
@@ -51,5 +51,8 @@ namespace Umbraco.Core.Models.Rdbms
[Column("dbType")]
public string DbType { get; set; }
+
+ [Column("UniqueID")]
+ public Guid UniqueId { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/UmbracoDeployChecksumDto.cs b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployChecksumDto.cs
new file mode 100644
index 0000000000..06d904ee88
--- /dev/null
+++ b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployChecksumDto.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("umbracoDeployChecksum")]
+ [PrimaryKey("id")]
+ [ExplicitColumns]
+ internal class UmbracoDeployChecksumDto
+ {
+ [Column("id")]
+ [PrimaryKeyColumn(Name = "PK_umbracoDeployChecksum")]
+ public int Id { get; set; }
+
+ [Column("entityType")]
+ [Length(32)]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoDeployChecksum", ForColumns = "entityType,entityGuid,entityPath")]
+ public string EntityType { get; set; }
+
+ [Column("entityGuid")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public Guid EntityGuid { get; set; }
+
+ [Column("entityPath")]
+ [Length(256)]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public string EntityPath { get; set; }
+
+ [Column("localChecksum")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Length(32)]
+ public string LocalChecksum { get; set; }
+
+ [Column("compositeChecksum")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ [Length(32)]
+ public string CompositeChecksum { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/UmbracoDeployDependencyDto.cs b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployDependencyDto.cs
new file mode 100644
index 0000000000..765a32c929
--- /dev/null
+++ b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployDependencyDto.cs
@@ -0,0 +1,26 @@
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("umbracoDeployDependency")]
+ [ExplicitColumns]
+ internal class UmbracoDeployDependencyDto
+ {
+ [Column("sourceId")]
+ [PrimaryKeyColumn(AutoIncrement = false, Clustered = true, Name = "PK_umbracoDeployDependency", OnColumns = "sourceId, targetId")]
+ [ForeignKey(typeof(UmbracoDeployChecksumDto), Name = "FK_umbracoDeployDependency_umbracoDeployChecksum_id1")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public int SourceId { get; set; }
+
+ [Column("targetId")]
+ [ForeignKey(typeof(UmbracoDeployChecksumDto), Name = "FK_umbracoDeployDependency_umbracoDeployChecksum_id2")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public int TargetId { get; set; }
+
+ [Column("mode")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public int Mode { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Template.cs b/src/Umbraco.Core/Models/Template.cs
index 4aca88f286..1c8b44b674 100644
--- a/src/Umbraco.Core/Models/Template.cs
+++ b/src/Umbraco.Core/Models/Template.cs
@@ -118,18 +118,6 @@ namespace Umbraco.Core.Models
return ApplicationContext.Current.Services.FileService.DetermineTemplateRenderingEngine(this);
}
- ///
- /// Method to call when Entity is being saved
- ///
- /// Created date is set and a Unique key is assigned
- internal override void AddingEntity()
- {
- base.AddingEntity();
-
- if (Key == Guid.Empty)
- Key = Guid.NewGuid();
- }
-
public void SetMasterTemplate(ITemplate masterTemplate)
{
if (masterTemplate == null)
diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs
index 4975ad3bd2..70ef056068 100644
--- a/src/Umbraco.Core/Models/UmbracoEntity.cs
+++ b/src/Umbraco.Core/Models/UmbracoEntity.cs
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
///
/// Implementation of the for internal use.
///
- internal class UmbracoEntity : Entity, IUmbracoEntity
+ public class UmbracoEntity : Entity, IUmbracoEntity
{
private int _creatorId;
private int _level;
diff --git a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
index edcc25ade8..987be2a276 100644
--- a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
+++ b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
@@ -11,6 +11,20 @@ namespace Umbraco.Core.Models
internal static class UmbracoEntityExtensions
{
+ public static bool HasChildren(this IUmbracoEntity entity)
+ {
+ if (entity.AdditionalData.ContainsKey("HasChildren"))
+ {
+ var convert = entity.AdditionalData["HasChildren"].TryConvertTo();
+ if (convert)
+ {
+ return convert.Result;
+ }
+ }
+ return false;
+ }
+
+
public static object GetAdditionalDataValueIgnoreCase(this IUmbracoEntity entity, string key, object defaultVal)
{
if (entity.AdditionalData.ContainsKeyIgnoreCase(key) == false) return defaultVal;
diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
index 37ad181682..2be2010301 100644
--- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
+++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
@@ -15,49 +15,49 @@ namespace Umbraco.Core.Models
///
/// Content Item Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.ContentItemType)]
+ [UmbracoObjectType(Constants.ObjectTypes.ContentItemType)]
[FriendlyName("Content Item Type")]
ContentItemType,
///
/// Root
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.SystemRoot)]
+ [UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
[FriendlyName("Root")]
ROOT,
///
/// Document
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Document, typeof(IContent))]
+ [UmbracoObjectType(Constants.ObjectTypes.Document, typeof(IContent))]
[FriendlyName("Document")]
Document,
///
/// Media
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Media, typeof(IMedia))]
+ [UmbracoObjectType(Constants.ObjectTypes.Media, typeof(IMedia))]
[FriendlyName("Media")]
Media,
///
/// Member Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.MemberType, typeof(IMemberType))]
+ [UmbracoObjectType(Constants.ObjectTypes.MemberType, typeof(IMemberType))]
[FriendlyName("Member Type")]
MemberType,
///
/// Template
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Template, typeof(ITemplate))]
+ [UmbracoObjectType(Constants.ObjectTypes.Template, typeof(ITemplate))]
[FriendlyName("Template")]
Template,
///
/// Member Group
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.MemberGroup)]
+ [UmbracoObjectType(Constants.ObjectTypes.MemberGroup)]
[FriendlyName("Member Group")]
MemberGroup,
@@ -65,50 +65,73 @@ namespace Umbraco.Core.Models
///
/// Content Item
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.ContentItem)]
+ [UmbracoObjectType(Constants.ObjectTypes.ContentItem)]
[FriendlyName("Content Item")]
ContentItem,
///
/// "Media Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.MediaType, typeof(IMediaType))]
+ [UmbracoObjectType(Constants.ObjectTypes.MediaType, typeof(IMediaType))]
[FriendlyName("Media Type")]
MediaType,
///
/// Document Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.DocumentType, typeof(IContentType))]
+ [UmbracoObjectType(Constants.ObjectTypes.DocumentType, typeof(IContentType))]
[FriendlyName("Document Type")]
DocumentType,
///
/// Recycle Bin
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.ContentRecycleBin)]
+ [UmbracoObjectType(Constants.ObjectTypes.ContentRecycleBin)]
[FriendlyName("Recycle Bin")]
RecycleBin,
///
/// Stylesheet
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Stylesheet)]
+ [UmbracoObjectType(Constants.ObjectTypes.Stylesheet)]
[FriendlyName("Stylesheet")]
Stylesheet,
///
/// Member
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Member, typeof(IMember))]
+ [UmbracoObjectType(Constants.ObjectTypes.Member, typeof(IMember))]
[FriendlyName("Member")]
Member,
///
/// Data Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.DataType, typeof(IDataTypeDefinition))]
+ [UmbracoObjectType(Constants.ObjectTypes.DataType, typeof(IDataTypeDefinition))]
[FriendlyName("Data Type")]
- DataType
+ DataType,
+
+ ///
+ /// Document type container
+ ///
+ [UmbracoObjectType(Constants.ObjectTypes.DocumentTypeContainer)]
+ [FriendlyName("Document Type Container")]
+ DocumentTypeContainer,
+
+ ///
+ /// Media type container
+ ///
+ [UmbracoObjectType(Constants.ObjectTypes.MediaTypeContainer)]
+ [FriendlyName("Media Type Container")]
+ MediaTypeContainer,
+
+ ///
+ /// Media type container
+ ///
+ [UmbracoObjectType(Constants.ObjectTypes.DataTypeContainer)]
+ [FriendlyName("Data Type Container")]
+ DataTypeContainer
+
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs
index bd670f3836..5b9f63cf48 100644
--- a/src/Umbraco.Core/Models/UserExtensions.cs
+++ b/src/Umbraco.Core/Models/UserExtensions.cs
@@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Threading;
+using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs
index 90d173b49a..a8a1e80eb7 100644
--- a/src/Umbraco.Core/ObjectExtensions.cs
+++ b/src/Umbraco.Core/ObjectExtensions.cs
@@ -1,15 +1,10 @@
using System;
using System.Collections;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
-using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
-using System.Runtime.Serialization;
-using System.Runtime.Serialization.Formatters.Binary;
-using System.Security;
using System.Xml;
namespace Umbraco.Core
@@ -51,7 +46,7 @@ namespace Umbraco.Core
public static Attempt TryConvertTo(this object input)
{
var result = TryConvertTo(input, typeof(T));
- if (!result.Success)
+ if (result.Success == false)
{
//just try a straight up conversion
try
@@ -64,7 +59,7 @@ namespace Umbraco.Core
return Attempt.Fail(e);
}
}
- return !result.Success ? Attempt.Fail() : Attempt.Succeed((T)result.Result);
+ return result.Success == false ? Attempt.Fail() : Attempt.Succeed((T)result.Result);
}
///
@@ -117,7 +112,7 @@ namespace Umbraco.Core
}
// we've already dealed with nullables, so any other generic types need to fall through
- if (!destinationType.IsGenericType)
+ if (destinationType.IsGenericType == false)
{
if (input is string)
{
@@ -335,11 +330,11 @@ namespace Umbraco.Core
return null; // we can't decide...
}
- private readonly static char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
+ private static readonly char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
private static string NormalizeNumberDecimalSeparator(string s)
{
- var normalized = System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator[0];
+ var normalized = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized);
}
@@ -442,7 +437,7 @@ namespace Umbraco.Core
{
var props = TypeDescriptor.GetProperties(o);
var d = new Dictionary();
- foreach (var prop in props.Cast().Where(x => !ignoreProperties.Contains(x.Name)))
+ foreach (var prop in props.Cast().Where(x => ignoreProperties.Contains(x.Name) == false))
{
var val = prop.GetValue(o);
if (val != null)
@@ -478,13 +473,13 @@ namespace Umbraco.Core
var items = (from object enumItem in enumerable let value = GetEnumPropertyDebugString(enumItem, levels) where value != null select value).Take(10).ToList();
- return items.Count() > 0
+ return items.Any()
? "{{ {0} }}".InvariantFormat(String.Join(", ", items))
: null;
}
var props = obj.GetType().GetProperties();
- if ((props.Count() == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2)
+ if ((props.Length == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2)
{
try
{
@@ -500,12 +495,12 @@ namespace Umbraco.Core
if (levels > -1)
{
var items =
- from propertyInfo in props
+ (from propertyInfo in props
let value = GetPropertyDebugString(propertyInfo, obj, levels)
where value != null
- select "{0}={1}".InvariantFormat(propertyInfo.Name, value);
+ select "{0}={1}".InvariantFormat(propertyInfo.Name, value)).ToArray();
- return items.Count() > 0
+ return items.Any()
? "[{0}]:{{ {1} }}".InvariantFormat(obj.GetType().Name, String.Join(", ", items))
: null;
}
diff --git a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
index fdd2759d76..54c7d8d2c9 100644
--- a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
@@ -1,98 +1,144 @@
using System;
+using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
+ // factory for
+ // IContentType (document types)
+ // IMediaType (media types)
+ // IMemberType (member types)
+ //
internal class ContentTypeFactory
{
- private readonly Guid _nodeObjectType;
+ #region IContentType
- public ContentTypeFactory(Guid nodeObjectType)
+ public IContentType BuildContentTypeEntity(ContentTypeDto dto)
{
- _nodeObjectType = nodeObjectType;
- }
+ var contentType = new ContentType(dto.NodeDto.ParentId);
+ BuildCommonEntity(contentType, dto);
- #region Implementation of IEntityFactory
-
- public IContentType BuildEntity(DocumentTypeDto dto)
- {
- var contentType = new ContentType(dto.ContentTypeDto.NodeDto.ParentId)
- {
- Id = dto.ContentTypeDto.NodeDto.NodeId,
- Key = dto.ContentTypeDto.NodeDto.UniqueId,
- Alias = dto.ContentTypeDto.Alias,
- Name = dto.ContentTypeDto.NodeDto.Text,
- Icon = dto.ContentTypeDto.Icon,
- Thumbnail = dto.ContentTypeDto.Thumbnail,
- SortOrder = dto.ContentTypeDto.NodeDto.SortOrder,
- Description = dto.ContentTypeDto.Description,
- CreateDate = dto.ContentTypeDto.NodeDto.CreateDate,
- Path = dto.ContentTypeDto.NodeDto.Path,
- Level = dto.ContentTypeDto.NodeDto.Level,
- CreatorId = dto.ContentTypeDto.NodeDto.UserId.Value,
- AllowedAsRoot = dto.ContentTypeDto.AllowAtRoot,
- IsContainer = dto.ContentTypeDto.IsContainer,
- Trashed = dto.ContentTypeDto.NodeDto.Trashed,
- DefaultTemplateId = dto.TemplateNodeId
- };
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
contentType.ResetDirtyProperties(false);
- return contentType;
- }
- public DocumentTypeDto BuildDto(IContentType entity)
- {
- var documentTypeDto = new DocumentTypeDto
- {ContentTypeDto = BuildContentTypeDto(entity), ContentTypeNodeId = entity.Id};
-
- var contentType = entity as ContentType;
- if(contentType != null)
- {
- documentTypeDto.TemplateNodeId = contentType.DefaultTemplateId;
- documentTypeDto.IsDefault = true;
- }
- return documentTypeDto;
+ return contentType;
}
#endregion
- private ContentTypeDto BuildContentTypeDto(IContentType entity)
+ #region IMediaType
+
+ public IMediaType BuildMediaTypeEntity(ContentTypeDto dto)
{
+ var contentType = new MediaType(dto.NodeDto.ParentId);
+ BuildCommonEntity(contentType, dto);
+
+ //on initial construction we don't want to have dirty properties tracked
+ // http://issues.umbraco.org/issue/U4-1946
+ contentType.ResetDirtyProperties(false);
+
+ return contentType;
+ }
+
+ #endregion
+
+ #region IMemberType
+
+ public IMemberType BuildMemberTypeEntity(ContentTypeDto dto)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable BuildMemberTypeDtos(IMemberType entity)
+ {
+ var memberType = entity as MemberType;
+ if (memberType == null || memberType.PropertyTypes.Any() == false)
+ return Enumerable.Empty();
+
+ var dtos = memberType.PropertyTypes.Select(x => new MemberTypeDto
+ {
+ NodeId = entity.Id,
+ PropertyTypeId = x.Id,
+ CanEdit = memberType.MemberCanEditProperty(x.Alias),
+ ViewOnProfile = memberType.MemberCanViewProperty(x.Alias)
+ }).ToList();
+ return dtos;
+ }
+
+ #endregion
+
+ #region Common
+
+ private static void BuildCommonEntity(ContentTypeBase entity, ContentTypeDto dto)
+ {
+ entity.Id = dto.NodeDto.NodeId;
+ entity.Key = dto.NodeDto.UniqueId;
+ entity.Alias = dto.Alias;
+ entity.Name = dto.NodeDto.Text;
+ entity.Icon = dto.Icon;
+ entity.Thumbnail = dto.Thumbnail;
+ entity.SortOrder = dto.NodeDto.SortOrder;
+ entity.Description = dto.Description;
+ entity.CreateDate = dto.NodeDto.CreateDate;
+ entity.Path = dto.NodeDto.Path;
+ entity.Level = dto.NodeDto.Level;
+ entity.CreatorId = dto.NodeDto.UserId.Value;
+ entity.AllowedAsRoot = dto.AllowAtRoot;
+ entity.IsContainer = dto.IsContainer;
+ entity.Trashed = dto.NodeDto.Trashed;
+ }
+
+ public ContentTypeDto BuildContentTypeDto(IContentTypeBase entity)
+ {
+ Guid nodeObjectType;
+ if (entity is IContentType)
+ nodeObjectType = Constants.ObjectTypes.DocumentTypeGuid;
+ else if (entity is IMediaType)
+ nodeObjectType = Constants.ObjectTypes.MediaTypeGuid;
+ else if (entity is IMemberType)
+ nodeObjectType = Constants.ObjectTypes.MemberTypeGuid;
+ else
+ throw new Exception("oops: invalid entity.");
+
var contentTypeDto = new ContentTypeDto
- {
- Alias = entity.Alias,
- Description = entity.Description,
- Icon = entity.Icon,
- Thumbnail = entity.Thumbnail,
- NodeId = entity.Id,
- AllowAtRoot = entity.AllowedAsRoot,
- IsContainer = entity.IsContainer,
- NodeDto = BuildNodeDto(entity)
- };
+ {
+ Alias = entity.Alias,
+ Description = entity.Description,
+ Icon = entity.Icon,
+ Thumbnail = entity.Thumbnail,
+ NodeId = entity.Id,
+ AllowAtRoot = entity.AllowedAsRoot,
+ IsContainer = entity.IsContainer,
+ NodeDto = BuildNodeDto(entity, nodeObjectType)
+ };
return contentTypeDto;
}
- private NodeDto BuildNodeDto(IContentType entity)
+ private static NodeDto BuildNodeDto(IUmbracoEntity entity, Guid nodeObjectType)
{
var nodeDto = new NodeDto
- {
- CreateDate = entity.CreateDate,
- NodeId = entity.Id,
- Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
- NodeObjectType = _nodeObjectType,
- ParentId = entity.ParentId,
- Path = entity.Path,
- SortOrder = entity.SortOrder,
- Text = entity.Name,
- Trashed = false,
- UniqueId = entity.Key,
- UserId = entity.CreatorId
- };
+ {
+ CreateDate = entity.CreateDate,
+ NodeId = entity.Id,
+ Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
+ NodeObjectType = nodeObjectType,
+ ParentId = entity.ParentId,
+ Path = entity.Path,
+ SortOrder = entity.SortOrder,
+ Text = entity.Name,
+ Trashed = false,
+ UniqueId = entity.Key,
+ UserId = entity.CreatorId
+ };
return nodeDto;
}
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs b/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs
index 14dca6b366..1b9d73bdd4 100644
--- a/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Factories
{
var text = new LanguageTextDto
{
- LanguageId = translation.Language.Id,
+ LanguageId = translation.LanguageId,
UniqueId = translation.Key,
Value = translation.Value
};
diff --git a/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs b/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs
index 8dca2494d0..65297b9529 100644
--- a/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs
@@ -7,20 +7,19 @@ namespace Umbraco.Core.Persistence.Factories
internal class DictionaryTranslationFactory
{
private readonly Guid _uniqueId;
- private ILanguage _language;
- public DictionaryTranslationFactory(Guid uniqueId, ILanguage language)
+ public DictionaryTranslationFactory(Guid uniqueId)
{
_uniqueId = uniqueId;
- _language = language;
}
#region Implementation of IEntityFactory
public IDictionaryTranslation BuildEntity(LanguageTextDto dto)
{
- var item = new DictionaryTranslation(_language, dto.Value, _uniqueId)
+ var item = new DictionaryTranslation(dto.LanguageId, dto.Value, _uniqueId)
{Id = dto.PrimaryKey};
+
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
item.ResetDirtyProperties(false);
@@ -31,7 +30,7 @@ namespace Umbraco.Core.Persistence.Factories
{
var text = new LanguageTextDto
{
- LanguageId = entity.Language.Id,
+ LanguageId = entity.LanguageId,
UniqueId = _uniqueId,
Value = entity.Value
};
diff --git a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs
deleted file mode 100644
index 98048cd3a7..0000000000
--- a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System;
-using System.Globalization;
-using Umbraco.Core.Models;
-using Umbraco.Core.Models.Rdbms;
-
-namespace Umbraco.Core.Persistence.Factories
-{
- internal class MediaTypeFactory
- {
- private readonly Guid _nodeObjectType;
-
- public MediaTypeFactory(Guid nodeObjectType)
- {
- _nodeObjectType = nodeObjectType;
- }
-
- #region Implementation of IEntityFactory
-
- public IMediaType BuildEntity(ContentTypeDto dto)
- {
- var contentType = new MediaType(dto.NodeDto.ParentId)
- {
- Id = dto.NodeDto.NodeId,
- Key = dto.NodeDto.UniqueId,
- Alias = dto.Alias,
- Name = dto.NodeDto.Text,
- Icon = dto.Icon,
- Thumbnail = dto.Thumbnail,
- SortOrder = dto.NodeDto.SortOrder,
- Description = dto.Description,
- CreateDate = dto.NodeDto.CreateDate,
- Path = dto.NodeDto.Path,
- Level = dto.NodeDto.Level,
- CreatorId = dto.NodeDto.UserId.Value,
- AllowedAsRoot = dto.AllowAtRoot,
- IsContainer = dto.IsContainer,
- Trashed = dto.NodeDto.Trashed
- };
- //on initial construction we don't want to have dirty properties tracked
- // http://issues.umbraco.org/issue/U4-1946
- contentType.ResetDirtyProperties(false);
- return contentType;
- }
-
- public ContentTypeDto BuildDto(IMediaType entity)
- {
- var contentTypeDto = new ContentTypeDto
- {
- Alias = entity.Alias,
- Description = entity.Description,
- Icon = entity.Icon,
- Thumbnail = entity.Thumbnail,
- NodeId = entity.Id,
- AllowAtRoot = entity.AllowedAsRoot,
- IsContainer = entity.IsContainer,
- NodeDto = BuildNodeDto(entity)
- };
- return contentTypeDto;
- }
-
- #endregion
-
- private NodeDto BuildNodeDto(IMediaType entity)
- {
- var nodeDto = new NodeDto
- {
- CreateDate = entity.CreateDate,
- NodeId = entity.Id,
- Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
- NodeObjectType = _nodeObjectType,
- ParentId = entity.ParentId,
- Path = entity.Path,
- SortOrder = entity.SortOrder,
- Text = entity.Name,
- Trashed = false,
- UniqueId = entity.Key,
- UserId = entity.CreatorId
- };
- return nodeDto;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs
deleted file mode 100644
index 5879016fa6..0000000000
--- a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using Umbraco.Core.Models;
-using Umbraco.Core.Models.Rdbms;
-
-namespace Umbraco.Core.Persistence.Factories
-{
- internal class MemberTypeFactory
- {
- private readonly Guid _nodeObjectType;
-
- public MemberTypeFactory(Guid nodeObjectType)
- {
- _nodeObjectType = nodeObjectType;
- }
-
- public IMemberType BuildEntity(ContentTypeDto dto)
- {
- throw new System.NotImplementedException();
- }
-
- public ContentTypeDto BuildDto(IMemberType entity)
- {
- var contentTypeDto = new ContentTypeDto
- {
- Alias = entity.Alias,
- Description = entity.Description,
- Icon = entity.Icon,
- Thumbnail = entity.Thumbnail,
- NodeId = entity.Id,
- AllowAtRoot = entity.AllowedAsRoot,
- IsContainer = entity.IsContainer,
- NodeDto = BuildNodeDto(entity)
- };
- return contentTypeDto;
- }
-
- public IEnumerable BuildMemberTypeDtos(IMemberType entity)
- {
- var memberType = entity as MemberType;
- if (memberType == null || memberType.PropertyTypes.Any() == false)
- return Enumerable.Empty();
-
- var memberTypes = new List();
- foreach (var propertyType in memberType.PropertyTypes)
- {
- memberTypes.Add(new MemberTypeDto
- {
- NodeId = entity.Id,
- PropertyTypeId = propertyType.Id,
- CanEdit = memberType.MemberCanEditProperty(propertyType.Alias),
- ViewOnProfile = memberType.MemberCanViewProperty(propertyType.Alias)
- });
- }
-
- return memberTypes;
- }
-
- private NodeDto BuildNodeDto(IMemberType entity)
- {
- var nodeDto = new NodeDto
- {
- CreateDate = entity.CreateDate,
- NodeId = entity.Id,
- Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
- NodeObjectType = _nodeObjectType,
- ParentId = entity.ParentId,
- Path = entity.Path,
- SortOrder = entity.SortOrder,
- Text = entity.Name,
- Trashed = false,
- UniqueId = entity.Key,
- UserId = entity.CreatorId
- };
- return nodeDto;
- }
-
- private int DeterminePropertyTypeId(int initialId, string alias, IEnumerable propertyTypes)
- {
- if (initialId == 0 || initialId == default(int))
- {
- var propertyType = propertyTypes.SingleOrDefault(x => x.Alias.Equals(alias));
- if (propertyType == null)
- return default(int);
-
- return propertyType.Id;
- }
-
- return initialId;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
index 38ef4542f4..468ea1b8bb 100644
--- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
@@ -51,33 +51,31 @@ namespace Umbraco.Core.Persistence.Factories
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
new MemberTypePropertyProfileAccess(false, false));
}
- memberType.PropertyTypes = propertyTypes;
+ memberType.NoGroupPropertyTypes = propertyTypes;
return memberType;
}
private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps)
{
- var propertyGroups = new PropertyGroupCollection();
-
+ // see PropertyGroupFactory, repeating code here...
+
+ var propertyGroups = new PropertyGroupCollection();
foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue))
{
var group = new PropertyGroup();
-
- //Only assign an Id if the PropertyGroup belongs to this ContentType
+
+ // if the group is defined on the current member type,
+ // assign its identifier, else it will be zero
if (groupDto.ContentTypeNodeId == memberType.Id)
{
+ // note: no idea why Id is nullable here, but better check
+ if (groupDto.Id.HasValue == false)
+ throw new Exception("oops: groupDto.Id has no value.");
group.Id = groupDto.Id.Value;
-
- if (groupDto.ParentGroupId.HasValue)
- group.ParentId = groupDto.ParentGroupId.Value;
- }
- else
- {
- //If the PropertyGroup is inherited, we add a reference to the group as a Parent.
- group.ParentId = groupDto.Id;
}
+ group.Key = groupDto.UniqueId;
group.Name = groupDto.Text;
group.SortOrder = groupDto.SortOrder;
group.PropertyTypes = new PropertyTypeCollection();
@@ -117,7 +115,8 @@ namespace Umbraco.Core.Persistence.Factories
ValidationRegExp = typeDto.ValidationRegExp,
PropertyGroupId = new Lazy(() => tempGroupDto.Id.Value),
CreateDate = memberType.CreateDate,
- UpdateDate = memberType.UpdateDate
+ UpdateDate = memberType.UpdateDate,
+ Key = typeDto.UniqueId
};
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -167,9 +166,10 @@ namespace Umbraco.Core.Persistence.Factories
Name = typeDto.Name,
SortOrder = typeDto.SortOrder,
ValidationRegExp = typeDto.ValidationRegExp,
- PropertyGroupId = new Lazy(() => default(int)),
+ PropertyGroupId = null,
CreateDate = dto.CreateDate,
- UpdateDate = dto.CreateDate
+ UpdateDate = dto.CreateDate,
+ Key = typeDto.UniqueId
};
propertyTypes.Add(propertyType);
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
index 4e3653bf9e..8d51b627ea 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
@@ -63,7 +63,9 @@ namespace Umbraco.Core.Persistence.Factories
//Check if property has an Id and set it, so that it can be updated if it already exists
if (property.HasIdentity)
+ {
dto.Id = property.Id;
+ }
if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer)
{
@@ -82,11 +84,21 @@ namespace Umbraco.Core.Persistence.Factories
}
}
}
+ else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Decimal && property.Value != null)
+ {
+ decimal val;
+ if (decimal.TryParse(property.Value.ToString(), out val))
+ {
+ dto.Decimal = val;
+ }
+ }
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date && property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false)
{
DateTime date;
- if(DateTime.TryParse(property.Value.ToString(), out date))
+ if (DateTime.TryParse(property.Value.ToString(), out date))
+ {
dto.Date = date;
+ }
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Ntext && property.Value != null)
{
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
index 443457b0d9..2dfb996bb3 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
@@ -8,21 +8,21 @@ namespace Umbraco.Core.Persistence.Factories
{
internal class PropertyGroupFactory
{
- private readonly int _id;
+ private readonly int _contentTypeId;
private readonly DateTime _createDate;
private readonly DateTime _updateDate;
//a callback to create a property type which can be injected via a contructor
private readonly Func _propertyTypeCtor;
- public PropertyGroupFactory(int id)
+ public PropertyGroupFactory(int contentTypeId)
{
- _id = id;
+ _contentTypeId = contentTypeId;
_propertyTypeCtor = (propertyEditorAlias, dbType, alias) => new PropertyType(propertyEditorAlias, dbType);
}
- public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate, Func propertyTypeCtor)
+ public PropertyGroupFactory(int contentTypeId, DateTime createDate, DateTime updateDate, Func propertyTypeCtor)
{
- _id = id;
+ _contentTypeId = contentTypeId;
_createDate = createDate;
_updateDate = updateDate;
_propertyTypeCtor = propertyTypeCtor;
@@ -30,29 +30,24 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory,IEnumerable>
- public IEnumerable BuildEntity(IEnumerable dto)
+ public IEnumerable BuildEntity(IEnumerable groupDtos)
{
+ // groupDtos contains all the groups, those that are defined on the current
+ // content type, and those that are inherited from composition content types
var propertyGroups = new PropertyGroupCollection();
- foreach (var groupDto in dto)
+ foreach (var groupDto in groupDtos)
{
var group = new PropertyGroup();
- //Only assign an Id if the PropertyGroup belongs to this ContentType
- if (groupDto.ContentTypeNodeId == _id)
- {
- group.Id = groupDto.Id;
- if (groupDto.ParentGroupId.HasValue)
- group.ParentId = groupDto.ParentGroupId.Value;
- }
- else
- {
- //If the PropertyGroup is inherited, we add a reference to the group as a Parent.
- group.ParentId = groupDto.Id;
- }
+ // if the group is defined on the current content type,
+ // assign its identifier, else it will be zero
+ if (groupDto.ContentTypeNodeId == _contentTypeId)
+ group.Id = groupDto.Id;
group.Name = groupDto.Text;
group.SortOrder = groupDto.SortOrder;
group.PropertyTypes = new PropertyTypeCollection();
+ group.Key = groupDto.UniqueId;
//Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded
var typeDtos = groupDto.PropertyTypeDtos.Where(x => x.Id > 0);
@@ -101,14 +96,12 @@ namespace Umbraco.Core.Persistence.Factories
{
var dto = new PropertyTypeGroupDto
{
- ContentTypeNodeId = _id,
+ ContentTypeNodeId = _contentTypeId,
SortOrder = propertyGroup.SortOrder,
- Text = propertyGroup.Name
+ Text = propertyGroup.Name,
+ UniqueId = propertyGroup.Key
};
- if (propertyGroup.ParentId.HasValue)
- dto.ParentGroupId = propertyGroup.ParentId.Value;
-
if (propertyGroup.HasIdentity)
dto.Id = propertyGroup.Id;
@@ -122,18 +115,14 @@ namespace Umbraco.Core.Persistence.Factories
var propertyTypeDto = new PropertyTypeDto
{
Alias = propertyType.Alias,
- ContentTypeId = _id,
+ ContentTypeId = _contentTypeId,
DataTypeId = propertyType.DataTypeDefinitionId,
Description = propertyType.Description,
Mandatory = propertyType.Mandatory,
Name = propertyType.Name,
SortOrder = propertyType.SortOrder,
ValidationRegExp = propertyType.ValidationRegExp,
- UniqueId = propertyType.HasIdentity
- ? propertyType.Key == Guid.Empty
- ? Guid.NewGuid()
- : propertyType.Key
- : Guid.NewGuid()
+ UniqueId = propertyType.Key
};
if (tabId != default(int))
diff --git a/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs
index d911db1967..655c40fc61 100644
--- a/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.Mappers
internal override void BuildMap()
{
CacheMap(src => src.Id, dto => dto.Id);
- CacheMap(src => src.ParentId, dto => dto.ParentGroupId);
+ CacheMap(src => src.Key, dto => dto.UniqueId);
CacheMap(src => src.SortOrder, dto => dto.SortOrder);
CacheMap(src => src.Name, dto => dto.Text);
}
diff --git a/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs
new file mode 100644
index 0000000000..4399fd323b
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs
@@ -0,0 +1,38 @@
+using System.Collections.Concurrent;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Mappers
+{
+ ///
+ /// Represents a to DTO mapper used to translate the properties of the public api
+ /// implementation to that of the database's DTO as sql: [tableName].[columnName].
+ ///
+ [MapperFor(typeof(TaskType))]
+ public sealed class TaskTypeMapper : BaseMapper
+ {
+ private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
+
+ //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
+ // otherwise that would fail because there is no public constructor.
+ public TaskTypeMapper()
+ {
+ BuildMap();
+ }
+
+ #region Overrides of BaseMapper
+
+ internal override ConcurrentDictionary PropertyInfoCache
+ {
+ get { return PropertyInfoCacheInstance; }
+ }
+
+ internal override void BuildMap()
+ {
+ CacheMap(src => src.Id, dto => dto.Id);
+ CacheMap(src => src.Alias, dto => dto.Alias);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs
index 08a7de0777..0d5b485ffa 100644
--- a/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs
@@ -39,6 +39,7 @@ namespace Umbraco.Core.Persistence.Mappers
{
CacheMap(src => src.Id, dto => dto.NodeId);
CacheMap(src => src.MasterTemplateId, dto => dto.ParentId);
+ CacheMap(src => src.Key, dto => dto.UniqueId);
CacheMap(src => src.Alias, dto => dto.Alias);
CacheMap(src => src.Content, dto => dto.Design);
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
index fbe7675836..8046de454b 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
@@ -108,7 +108,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -21, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,-21", SortOrder = 0, UniqueId = new Guid("BF7C7CBC-952F-4518-97A2-69E9C7B33842"), Text = "Recycle Bin", NodeObjectType = new Guid(Constants.ObjectTypes.MediaRecycleBin), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -92, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-92", SortOrder = 35, UniqueId = new Guid("f0bc4bfb-b499-40d6-ba86-058885a5178c"), Text = "Label", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -90, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-90", SortOrder = 34, UniqueId = new Guid("84c6b441-31df-4ffe-b67e-67d5bc3ae65a"), Text = "Upload", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
- _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -89, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-89", SortOrder = 33, UniqueId = new Guid("c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3"), Text = "Textbox multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
+ _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -89, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-89", SortOrder = 33, UniqueId = new Guid("c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3"), Text = "Textarea", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -88, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-88", SortOrder = 32, UniqueId = new Guid("0cc0eba1-9960-42c9-bf9b-60e150b429ae"), Text = "Textstring", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -87, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-87", SortOrder = 4, UniqueId = new Guid("ca90c950-0aff-4e72-b976-a30b1ac57dad"), Text = "Richtext editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -51, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-51", SortOrder = 2, UniqueId = new Guid("2e6d3631-066e-44b8-aec4-96f09099b2b5"), Text = "Numeric", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
@@ -117,8 +117,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -42, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-42", SortOrder = 2, UniqueId = new Guid("0b6a45e7-44ba-430d-9da5-4e46060b9e03"), Text = "Dropdown", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = new Guid("5046194e-4237-453c-a547-15db3a07c4e1"), Text = "Date Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -40, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-40", SortOrder = 2, UniqueId = new Guid("bb5f57c9-ce2b-4bb9-b697-4caca783a805"), Text = "Radiobox", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
- _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -39, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-39", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
- _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -38, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-38", SortOrder = 2, UniqueId = new Guid("fd9f1447-6c61-4a7c-9595-5aa39147d318"), Text = "Folder Browser", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
+ _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -39, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-39", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -37, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-37", SortOrder = 2, UniqueId = new Guid("0225af17-b302-49cb-9176-b9f35cab9c17"), Text = "Approved Color", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -36, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-36", SortOrder = 2, UniqueId = new Guid("e4d66c0f-b935-4200-81f0-025f7256b89a"), Text = "Date Picker with time", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = Constants.System.DefaultContentListViewDataTypeId, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-95", SortOrder = 2, UniqueId = new Guid("C0808DD3-8133-4E4B-8CE8-E2BEA84A96A4"), Text = Constants.Conventions.DataTypes.ListViewPrefix + "Content", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
@@ -135,13 +134,16 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1043, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1043", SortOrder = 2, UniqueId = new Guid("1df9f033-e6d4-451f-b8d2-e0cbc50a836f"), Text = "Image Cropper", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1045, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1045", SortOrder = 2, UniqueId = new Guid("7E3962CC-CE20-4FFC-B661-5897A894BA7E"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
-
+
//TODO: We're not creating these for 7.0
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1039, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1039", SortOrder = 2, UniqueId = new Guid("06f349a9-c949-4b6a-8660-59c10451af42"), Text = "Ultimate Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1038, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1038", SortOrder = 2, UniqueId = new Guid("1251c96c-185c-4e9b-93f4-b48205573cbd"), Text = "Simple Editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
-
+
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1042, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1042", SortOrder = 2, UniqueId = new Guid("0a452bd5-83f9-4bc3-8403-1286e13fb77e"), Text = "Macro Container", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
+
+ // all lock objects
+ _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = Constants.System.ServersLock, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1," + Constants.System.ServersLock, SortOrder = 1, UniqueId = new Guid("0AF5E610-A310-4B6F-925F-E928D5416AF7"), Text = "LOCK: Servers", NodeObjectType = Constants.ObjectTypes.LockObjectGuid, CreateDate = DateTime.Now });
}
private void CreateCmsContentTypeData()
@@ -179,16 +181,16 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
private void CreateCmsPropertyTypeGroupData()
{
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1 });
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1 });
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1 });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Image) });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.File) });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Contents) });
//membership property group
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1 });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership) });
}
private void CreateCmsPropertyTypeData()
{
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = -90, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.File, Name = "Upload image", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = 1043, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.File, Name = "Upload image", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 7, UniqueId = 7.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Width, Name = "Width", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 8, UniqueId = 8.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Height, Name = "Height", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 9, UniqueId = 9.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
@@ -196,7 +198,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = -90, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 27, UniqueId = 27.ToGuid(), DataTypeId = -38, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 27, UniqueId = 27.ToGuid(), DataTypeId = Constants.System.DefaultMediaListViewDataTypeId, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
//membership property types
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = -89, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.Comments, Name = Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.FailedPasswordAttempts, Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null });
@@ -231,16 +233,15 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 6, DataTypeId = -90, PropertyEditorAlias = Constants.PropertyEditors.UploadFieldAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 7, DataTypeId = -92, PropertyEditorAlias = Constants.PropertyEditors.NoEditAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 8, DataTypeId = -36, PropertyEditorAlias = Constants.PropertyEditors.DateTimeAlias, DbType = "Date" });
- _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" });
- _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 10, DataTypeId = -38, PropertyEditorAlias = Constants.PropertyEditors.FolderBrowserAlias, DbType = "Nvarchar" });
+ _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 11, DataTypeId = -39, PropertyEditorAlias = Constants.PropertyEditors.DropDownListMultipleAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -40, PropertyEditorAlias = Constants.PropertyEditors.RadioButtonListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, PropertyEditorAlias = Constants.PropertyEditors.DateAlias, DbType = "Date" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 14, DataTypeId = -42, PropertyEditorAlias = Constants.PropertyEditors.DropDownListAlias, DbType = "Integer" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, PropertyEditorAlias = Constants.PropertyEditors.CheckBoxListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 16, DataTypeId = 1034, PropertyEditorAlias = Constants.PropertyEditors.ContentPickerAlias, DbType = "Integer" });
- _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 17, DataTypeId = 1035, PropertyEditorAlias = Constants.PropertyEditors.MediaPickerAlias, DbType = "Integer" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 18, DataTypeId = 1036, PropertyEditorAlias = Constants.PropertyEditors.MemberPickerAlias, DbType = "Integer" });
+ _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 17, DataTypeId = 1035, PropertyEditorAlias = Constants.PropertyEditors.MultipleMediaPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 21, DataTypeId = 1040, PropertyEditorAlias = Constants.PropertyEditors.RelatedLinksAlias, DbType = "Ntext" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 22, DataTypeId = 1041, PropertyEditorAlias = Constants.PropertyEditors.TagsAlias, DbType = "Ntext" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 24, DataTypeId = 1043, PropertyEditorAlias = Constants.PropertyEditors.ImageCropperAlias, DbType = "Ntext" });
@@ -253,7 +254,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 19, DataTypeId = 1038, PropertyEditorAlias = Constants.PropertyEditors.MarkdownEditorAlias, DbType = "Ntext" });
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 20, DataTypeId = 1039, PropertyEditorAlias = Constants.PropertyEditors.UltimatePickerAlias, DbType = "Ntext" });
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 23, DataTypeId = 1042, PropertyEditorAlias = Constants.PropertyEditors.MacroContainerAlias, DbType = "Ntext" });
-
}
private void CreateCmsDataTypePreValuesData()
@@ -269,6 +269,17 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "Name" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -3, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "asc" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -4, Alias = "includeProperties", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "[{\"alias\":\"email\",\"isSystem\":1},{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1}]" });
+
+ //layouts for the list view
+ var cardLayout = "{\"name\": \"Grid\",\"path\": \"views/propertyeditors/listview/layouts/grid/grid.html\", \"icon\": \"icon-thumbnails-small\", \"isSystem\": 1, \"selected\": true}";
+ var listLayout = "{\"name\": \"List\",\"path\": \"views/propertyeditors/listview/layouts/list/list.html\",\"icon\": \"icon-list\", \"isSystem\": 1,\"selected\": true}";
+
+ //defaults for the media list
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -5, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "100" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -6, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "VersionDate" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -7, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "desc" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -8, Alias = "layouts", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[" + cardLayout + "," + listLayout + "]" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"sortOrder\",\"isSystem\":1, \"header\": \"Sort order\"},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
}
private void CreateUmbracoRelationTypeData()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
index 0ba269a3ba..0ae044d1ee 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
@@ -44,7 +44,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{4, typeof (ContentVersionDto)},
{5, typeof (DocumentDto)},
- {6, typeof (DocumentTypeDto)},
+ {6, typeof (ContentTypeTemplateDto)},
{7, typeof (DataTypeDto)},
{8, typeof (DataTypePreValueDto)},
{9, typeof (DictionaryDto)},
@@ -82,7 +82,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{42, typeof (AccessRuleDto)},
{43, typeof(CacheInstructionDto)},
{44, typeof (ExternalLoginDto)},
- {45, typeof (MigrationDto)}
+ {45, typeof (MigrationDto)},
+ {46, typeof (UmbracoDeployChecksumDto)},
+ {47, typeof (UmbracoDeployDependencyDto)}
};
#endregion
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
index 04d6598d4b..9af6d46fbb 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
@@ -119,6 +119,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
return new Version(7, 2, 5);
}
+ //if the error is for umbracoDeployChecksum it must be the previous version to 7.4 since that is when it is added
+ if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoDeployChecksum"))))
+ {
+ return new Version(7, 3, 4);
+ }
+
return UmbracoVersion.Current;
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
index 234cd67386..081865b9a1 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
@@ -27,18 +28,21 @@ namespace Umbraco.Core.Persistence.Migrations
private readonly IMigration[] _migrations;
[Obsolete("Use the ctor that specifies all dependencies instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public MigrationRunner(Version currentVersion, Version targetVersion, string productName)
: this(LoggerResolver.Current.Logger, currentVersion, targetVersion, productName)
{
}
[Obsolete("Use the ctor that specifies all dependencies instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName)
: this(logger, currentVersion, targetVersion, productName, null)
{
}
[Obsolete("Use the ctor that specifies all dependencies instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName, params IMigration[] migrations)
: this(ApplicationContext.Current.Services.MigrationEntryService, logger, new SemVersion(currentVersion), new SemVersion(targetVersion), productName, migrations)
{
@@ -92,7 +96,7 @@ namespace Umbraco.Core.Persistence.Migrations
: OrderedDowngradeMigrations(foundMigrations).ToList();
- if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
+ if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, _productName, true), this))
{
_logger.Warn("Migration was cancelled by an event");
return false;
@@ -121,7 +125,7 @@ namespace Umbraco.Core.Persistence.Migrations
throw;
}
- Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, false), this);
+ Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, _productName, false), this);
return true;
}
@@ -289,7 +293,7 @@ namespace Umbraco.Core.Persistence.Migrations
//NOTE: We CANNOT do this as part of the transaction!!! This is because when upgrading to 7.3, we cannot
// create the migrations table and then add data to it in the same transaction without issuing things like GO
// commands and since we need to support all Dbs, we need to just do this after the fact.
- var exists = _migrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, _targetVersion);
+ var exists = _migrationEntryService.FindEntry(_productName, _targetVersion);
if (exists == null)
{
_migrationEntryService.CreateEntry(_productName, _targetVersion);
@@ -308,4 +312,4 @@ namespace Umbraco.Core.Persistence.Migrations
///
public static event TypedEventHandler Migrated;
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourOneZero/AddPreviewXmlTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourOneZero/AddPreviewXmlTable.cs
new file mode 100644
index 0000000000..4e8d3165fb
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourOneZero/AddPreviewXmlTable.cs
@@ -0,0 +1,32 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourOneZero
+{
+ [Migration("4.1.0", 0, GlobalSettings.UmbracoMigrationName)]
+ public class AddPreviewXmlTable : MigrationBase
+ {
+ public AddPreviewXmlTable(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ var tableName = "cmsPreviewXml";
+ var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
+ if (tables.InvariantContains(tableName)) return;
+
+ Create.Table(tableName)
+ .WithColumn("nodeId").AsInt32().NotNullable()
+ .WithColumn("versionId").AsGuid().NotNullable()
+ .WithColumn("timestamp").AsDateTime().NotNullable()
+ .WithColumn("xml").AsString();
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
index 830adfd7fb..749996ea36 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
@@ -32,17 +32,19 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
if (database != null)
{
- var dtSql = new Sql().Select("nodeId").From().Where(dto => dto.PropertyEditorAlias == Constants.PropertyEditors.RelatedLinksAlias);
+ var dtSql = new Sql().Select("nodeId")
+ .From(SqlSyntax)
+ .Where(dto => dto.PropertyEditorAlias == Constants.PropertyEditors.RelatedLinksAlias);
+
var dataTypeIds = database.Fetch(dtSql);
+ if (dataTypeIds.Any() == false) return string.Empty;
- if (!dataTypeIds.Any()) return string.Empty;
+ // need to use dynamic, as PropertyDataDto has new properties (eg decimal...) in further versions that don't exist yet
+ var propertyData = database.Fetch("SELECT * FROM cmsPropertyData"
+ + " WHERE propertyTypeId in (SELECT id from cmsPropertyType where dataTypeID IN (@dataTypeIds))", new { /*dataTypeIds =*/ dataTypeIds });
+ if (propertyData.Any() == false) return string.Empty;
- var propertyData =
- database.Fetch(
- "WHERE propertyTypeId in (SELECT id from cmsPropertyType where dataTypeID IN (@dataTypeIds))", new { dataTypeIds = dataTypeIds });
- if (!propertyData.Any()) return string.Empty;
-
- var nodesIdsWithProperty = propertyData.Select(x => x.NodeId).Distinct().ToArray();
+ var nodesIdsWithProperty = propertyData.Select(x => (int) x.contentNodeId).Distinct().ToArray();
var cmsContentXmlEntries = new List();
//We're doing an "IN" query here but SQL server only supports 2100 query parameters so we're going to split on that
@@ -54,30 +56,33 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
cmsContentXmlEntries.AddRange(database.Fetch("WHERE nodeId in (@nodeIds)", new { nodeIds = batch }));
}
- var propertyTypeIds = propertyData.Select(x => x.PropertyTypeId).Distinct();
+ var propertyTypeIds = propertyData.Select(x => (int) x.propertytypeid).Distinct();
//NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns
// won't exist yet
var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE id in (@propertyTypeIds)", new { propertyTypeIds = propertyTypeIds });
-
+
foreach (var data in propertyData)
{
- if (string.IsNullOrEmpty(data.Text) == false)
+ if (string.IsNullOrEmpty(data.dataNtext) == false)
{
XmlDocument xml;
//fetch the current data (that's in xml format)
try
{
xml = new XmlDocument();
- xml.LoadXml(data.Text);
+ xml.LoadXml(data.dataNtext);
}
- catch (Exception ex)
+ catch (Exception ex)
{
- Logger.Error("The data stored for property id " + data.Id + " on document " + data.NodeId +
- " is not valid XML, the data will be removed because it cannot be converted to the new format. The value was: " + data.Text, ex);
+ int dataId = data.id;
+ int dataNodeId = data.contentNodeId;
+ string dataText = data.dataNtext;
+ Logger.Error("The data stored for property id " + dataId + " on document " + dataNodeId +
+ " is not valid XML, the data will be removed because it cannot be converted to the new format. The value was: " + dataText, ex);
- data.Text = "";
- database.Update(data);
+ data.dataNtext = "";
+ database.Update("cmsPropertyData", "id", data, new[] { "dataNText" });
UpdateXmlTable(propertyTypes, data, cmsContentXmlEntries, database);
@@ -91,11 +96,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
var title = node.Attributes["title"].Value;
var type = node.Attributes["type"].Value;
- var newwindow = node.Attributes["newwindow"].Value.Equals("1") ? true : false;
+ var newwindow = node.Attributes["newwindow"].Value.Equals("1");
var lnk = node.Attributes["link"].Value;
//create the links in the format the new prop editor expects it to be
- var link = new ExpandoObject() as IDictionary;
+ var link = new ExpandoObject() as IDictionary;
link.Add("title", title);
link.Add("caption", title);
link.Add("link", lnk);
@@ -110,9 +115,9 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
}
//store the serialized data
- data.Text = JsonConvert.SerializeObject(links);
+ data.dataNtext = JsonConvert.SerializeObject(links);
- database.Update(data);
+ database.Update("cmsPropertyData", "id", data, new[] { "dataNText" });
UpdateXmlTable(propertyTypes, data, cmsContentXmlEntries, database);
@@ -127,20 +132,20 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified");
}
- private static void UpdateXmlTable(List propertyTypes, PropertyDataDto data, List cmsContentXmlEntries, Database database)
+ private static void UpdateXmlTable(List propertyTypes, dynamic data, List cmsContentXmlEntries, Database database)
{
//now we need to update the cmsContentXml table
- var propertyType = propertyTypes.SingleOrDefault(x => x.Id == data.PropertyTypeId);
+ var propertyType = propertyTypes.SingleOrDefault(x => x.Id == data.propertytypeid);
if (propertyType != null)
{
- var xmlItem = cmsContentXmlEntries.SingleOrDefault(x => x.NodeId == data.NodeId);
+ var xmlItem = cmsContentXmlEntries.SingleOrDefault(x => x.NodeId == data.contentNodeId);
if (xmlItem != null)
{
var x = XElement.Parse(xmlItem.Xml);
var prop = x.Element(propertyType.Alias);
if (prop != null)
{
- prop.ReplaceAll(new XCData(data.Text));
+ prop.ReplaceAll(new XCData(data.dataNtext));
database.Update(xmlItem);
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/EnsureServersLockObject.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/EnsureServersLockObject.cs
new file mode 100644
index 0000000000..6f9d74e5db
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/EnsureServersLockObject.cs
@@ -0,0 +1,57 @@
+using System;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero
+{
+ // This migration exists for 7.3.0 but it seems like it was not always running properly
+ // if you're upgrading from 7.3.0 or higher than we add this migration, if you're upgrading
+ // from 7.3.0 or lower then you will already get this migration in the migration to get to 7.3.0
+ [Migration("7.3.0", "7.5.0", 10, GlobalSettings.UmbracoMigrationName)]
+ public class EnsureServersLockObject : MigrationBase
+ {
+ public EnsureServersLockObject(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ // that lock object should have been part of BaseDataCreation since 7.3.0 but
+ // for some reason it was not, so it was created during migrations but not during
+ // new installs, so for ppl that upgrade, make sure they have it
+
+ EnsureLockObject(Constants.System.ServersLock, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
+ }
+
+ public override void Down()
+ {
+ // not implemented
+ }
+
+ private void EnsureLockObject(int id, string uniqueId, string text)
+ {
+ var exists = Context.Database.Exists(id);
+ if (exists) return;
+
+ Insert
+ .IntoTable("umbracoNode")
+ .EnableIdentityInsert()
+ .Row(new
+ {
+ id = id, // NodeId
+ trashed = false,
+ parentId = -1,
+ nodeUser = 0,
+ level = 1,
+ path = "-1," + id,
+ sortOrder = 0,
+ uniqueId = new Guid(uniqueId),
+ text = text,
+ nodeObjectType = new Guid(Constants.ObjectTypes.LockObject),
+ createDate = DateTime.Now
+ });
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs
new file mode 100644
index 0000000000..442b92d2b5
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs
@@ -0,0 +1,29 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 1, GlobalSettings.UmbracoMigrationName)]
+ public class AddDataDecimalColumn : MigrationBase
+ {
+ public AddDataDecimalColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ //Don't exeucte if the column is already there
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+
+ if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyData") && x.ColumnName.InvariantEquals("dataDecimal")) == false)
+ Create.Column("dataDecimal").OnTable("cmsPropertyData").AsDecimal().Nullable();
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs
new file mode 100644
index 0000000000..a39cea2ee0
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs
@@ -0,0 +1,48 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 5, GlobalSettings.UmbracoMigrationName)]
+ public class AddUmbracoDeployTables : MigrationBase
+ {
+ public AddUmbracoDeployTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ //Don't exeucte if the table is already there
+ var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
+ if (tables.InvariantContains("umbracoDeployChecksum")) return;
+
+ Create.Table("umbracoDeployChecksum")
+ .WithColumn("id").AsInt32().Identity().PrimaryKey("PK_umbracoDeployChecksum")
+ .WithColumn("entityType").AsString(32).NotNullable()
+ .WithColumn("entityGuid").AsGuid().Nullable()
+ .WithColumn("entityPath").AsString(256).Nullable()
+ .WithColumn("localChecksum").AsString(32).NotNullable()
+ .WithColumn("compositeChecksum").AsString(32).Nullable();
+
+ Create.Table("umbracoDeployDependency")
+ .WithColumn("sourceId").AsInt32().NotNullable().ForeignKey("FK_umbracoDeployDependency_umbracoDeployChecksum_id1", "umbracoDeployChecksum", "id")
+ .WithColumn("targetId").AsInt32().NotNullable().ForeignKey("FK_umbracoDeployDependency_umbracoDeployChecksum_id2", "umbracoDeployChecksum", "id")
+ .WithColumn("mode").AsInt32().NotNullable();
+
+ Create.PrimaryKey("PK_umbracoDeployDependency").OnTable("umbracoDeployDependency").Columns(new[] {"sourceId", "targetId"});
+
+ Create.Index("IX_umbracoDeployChecksum").OnTable("umbracoDeployChecksum")
+ .OnColumn("entityType")
+ .Ascending()
+ .OnColumn("entityGuid")
+ .Ascending()
+ .OnColumn("entityPath")
+ .Unique();
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs
new file mode 100644
index 0000000000..fe600f6b69
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 2, GlobalSettings.UmbracoMigrationName)]
+ public class AddUniqueIdPropertyTypeGroupColumn : MigrationBase
+ {
+ public AddUniqueIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ // don't execute if the column is already there
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+
+ if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyTypeGroup") && x.ColumnName.InvariantEquals("uniqueID")) == false)
+ {
+ Create.Column("uniqueID").OnTable("cmsPropertyTypeGroup").AsGuid().NotNullable().WithDefault(SystemMethods.NewGuid);
+
+ // unique constraint on name + version
+ Create.Index("IX_cmsPropertyTypeGroupUniqueID").OnTable("cmsPropertyTypeGroup")
+ .OnColumn("uniqueID").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .WithOptions()
+ .Unique();
+
+ // fill in the data in a way that is consistent over all environments
+ // (ie cannot use random guids, http://issues.umbraco.org/issue/U4-6942)
+ Execute.Code(UpdateGuids);
+ }
+ }
+
+ private static string UpdateGuids(Database database)
+ {
+ var updates = new List>();
+
+ foreach (var data in database.Query(@"
+SELECT cmsPropertyTypeGroup.id grId, cmsPropertyTypeGroup.text grName, cmsContentType.alias ctAlias, umbracoNode.nodeObjectType nObjType
+FROM cmsPropertyTypeGroup
+INNER JOIN cmsContentType
+ON cmsPropertyTypeGroup.contentTypeNodeId = cmsContentType.nodeId
+INNER JOIN umbracoNode
+ON cmsContentType.nodeId = umbracoNode.id"))
+ {
+ Guid guid;
+ // see BaseDataCreation... built-in groups have their own guids
+ if (data.grId == 3)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.Image);
+ }
+ else if (data.grId == 4)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.File);
+ }
+ else if (data.grId == 5)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.Contents);
+ }
+ else if (data.grId == 11)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.Membership);
+ }
+ else
+ {
+ // create a consistent guid from
+ // group name + content type alias + object type
+ string guidSource = data.grName + data.ctAlias + data.nObjType;
+ guid = guidSource.ToGuid();
+ }
+
+ // set the Unique Id to the one we've generated
+ // but not within the foreach loop (as we already have a data reader open)
+ updates.Add(Tuple.Create(guid, data.grId));
+ }
+
+ foreach (var update in updates)
+ database.Execute("UPDATE cmsPropertyTypeGroup SET uniqueID=@uid WHERE id=@id", new { uid = update.Item1, id = update.Item2 });
+
+ return string.Empty;
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs
new file mode 100644
index 0000000000..20200a3230
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ ///
+ /// Courier on v. 7.4+ will handle ContentTypes using GUIDs instead of
+ /// alias, so we need to ensure that these are initially consistent on
+ /// all environments (based on the alias).
+ ///
+ [Migration("7.4.0", 3, GlobalSettings.UmbracoMigrationName)]
+ public class EnsureContentTypeUniqueIdsAreConsistent : MigrationBase
+ {
+ public EnsureContentTypeUniqueIdsAreConsistent(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ var objectTypes = new[]
+ {
+ Constants.ObjectTypes.DocumentTypeGuid,
+ Constants.ObjectTypes.MediaTypeGuid,
+ Constants.ObjectTypes.MemberTypeGuid,
+ };
+
+ var sql = new Sql()
+ .Select("umbracoNode.id,cmsContentType.alias,umbracoNode.nodeObjectType")
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax)
+ .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId)
+ .WhereIn(x => x.NodeObjectType, objectTypes);
+
+ var rows = Context.Database.Fetch(sql);
+
+ foreach (var row in rows)
+ {
+ // create a consistent guid from
+ // alias + object type
+ var guidSource = ((string) row.alias) + ((Guid) row.nodeObjectType);
+ var guid = guidSource.ToGuid();
+
+ // set the Unique Id to the one we've generated
+ Update.Table("umbracoNode").Set(new { uniqueID = guid }).Where(new { id = row.id });
+ }
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs
new file mode 100644
index 0000000000..e1fa9e9257
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs
@@ -0,0 +1,34 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 4, GlobalSettings.UmbracoMigrationName)]
+ public class FixListViewMediaSortOrder : MigrationBase
+ {
+ public FixListViewMediaSortOrder(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ var mediaListviewIncludeProperties = Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax).Where(x => x.Id == -9)).FirstOrDefault();
+ if (mediaListviewIncludeProperties != null)
+ {
+ if (mediaListviewIncludeProperties.Value.Contains("\"alias\":\"sort\""))
+ {
+ mediaListviewIncludeProperties.Value = mediaListviewIncludeProperties.Value.Replace("\"alias\":\"sort\"", "\"alias\":\"sortOrder\"");
+ Context.Database.InsertOrUpdate(mediaListviewIncludeProperties);
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs
new file mode 100644
index 0000000000..3d285c2715
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs
@@ -0,0 +1,40 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 4, GlobalSettings.UmbracoMigrationName)]
+ public class RemoveParentIdPropertyTypeGroupColumn : MigrationBase
+ {
+ public RemoveParentIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ // don't execute if the column is not there anymore
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+
+ if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyTypeGroup") && x.ColumnName.InvariantEquals("parentGroupId")) == false)
+ return;
+
+ //This constraing can be based on old aliases, before removing them, check they exist
+ var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
+ if (constraints.Any(x => x.Item1.InvariantEquals("cmsPropertyTypeGroup") && x.Item3.InvariantEquals("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup_id")))
+ {
+ Delete.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup_id").OnTable("cmsPropertyTypeGroup");
+ }
+ if (constraints.Any(x => x.Item1.InvariantEquals("cmsPropertyTypeGroup") && x.Item3.InvariantEquals("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup")))
+ {
+ Delete.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup").OnTable("cmsPropertyTypeGroup");
+ }
+
+ Delete.Column("parentGroupId").FromTable("cmsPropertyTypeGroup");
+ }
+
+ public override void Down()
+ { }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs
new file mode 100644
index 0000000000..50f78ca66d
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs
@@ -0,0 +1,38 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeOne
+{
+ ///
+ /// This fixes the storage of user languages from the old format like en_us to en-US
+ ///
+ [Migration("7.3.1", 0, GlobalSettings.UmbracoMigrationName)]
+ public class UpdateUserLanguagesToIsoCode : MigrationBase
+ {
+ public UpdateUserLanguagesToIsoCode(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ var userData = Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax));
+ foreach (var user in userData.Where(x => x.UserLanguage.Contains("_")))
+ {
+ var languageParts = user.UserLanguage.Split('_');
+ if (languageParts.Length == 2)
+ {
+ Update.Table("umbracoUser")
+ .Set(new {userLanguage = languageParts[0] + "-" + languageParts[1].ToUpperInvariant()})
+ .Where(new {id = user.Id});
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs
new file mode 100644
index 0000000000..91b4bd6438
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeTwo
+{
+ ///
+ /// This reinserts all migrations in the migrations table to account for initial rows inserted
+ /// on creation without identity enabled.
+ ///
+ [Migration("7.3.2", 0, GlobalSettings.UmbracoMigrationName)]
+ public class EnsureMigrationsTableIdentityIsCorrect : MigrationBase
+ {
+ public EnsureMigrationsTableIdentityIsCorrect(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ // Due to the delayed execution of migrations, we have to wrap this code in Execute.Code to ensure the previous
+ // migration steps (esp. creating the migrations table) have completed before trying to fetch migrations from
+ // this table.
+ List migrations = null;
+ Execute.Code(db =>
+ {
+ migrations = Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax));
+ return string.Empty;
+ });
+ Delete.FromTable("umbracoMigration").AllRows();
+ Execute.Code(database =>
+ {
+ if (migrations != null)
+ {
+ foreach (var migration in migrations)
+ {
+ database.Insert("umbracoMigration", "id", true,
+ new {name = migration.Name, createDate = migration.CreateDate, version = migration.Version});
+ }
+ }
+ return string.Empty;
+ });
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs
index f2f9261f52..7589c7000d 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs
@@ -5,14 +5,13 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
-{
+{
[Migration("7.3.0", 13, GlobalSettings.UmbracoMigrationName)]
public class AddUniqueIdPropertyTypeColumn : MigrationBase
{
public AddUniqueIdPropertyTypeColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
- {
- }
+ { }
public override void Up()
{
@@ -23,7 +22,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
{
Create.Column("uniqueID").OnTable("cmsPropertyType").AsGuid().NotNullable().WithDefault(SystemMethods.NewGuid);
- //unique constraint on name + version
+ // unique constraint on name + version
Create.Index("IX_cmsPropertyTypeUniqueID").OnTable("cmsPropertyType")
.OnColumn("uniqueID").Ascending()
.WithOptions()
@@ -31,29 +30,29 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
.WithOptions()
.Unique();
- //now we need to fill in the data so that it is consistent, we can't have it generating random GUIDs for
- // the already existing data, see: http://issues.umbraco.org/issue/U4-6942
+ // fill in the data in a way that is consistent over all environments
+ // (ie cannot use random guids, http://issues.umbraco.org/issue/U4-6942)
foreach (var data in Context.Database.Query(@"
-SELECT cmsPropertyType.id ptId, cmsPropertyType.Alias ptAlias, cmsContentType.alias ctAlias
+SELECT cmsPropertyType.id ptId, cmsPropertyType.Alias ptAlias, cmsContentType.alias ctAlias, umbracoNode.nodeObjectType nObjType
FROM cmsPropertyType
INNER JOIN cmsContentType
-ON cmsPropertyType.contentTypeId = cmsContentType.nodeId"))
+ON cmsPropertyType.contentTypeId = cmsContentType.nodeId
+INNER JOIN umbracoNode
+ON cmsContentType.nodeId = umbracoNode.id"))
{
- //create a guid from the concatenation of the property type alias + the doc type alias
- string concatAlias = data.ptAlias + data.ctAlias;
- var ptGuid = concatAlias.ToGuid();
+ // create a consistent guid from
+ // property alias + content type alias + object type
+ string guidSource = data.ptAlias + data.ctAlias + data.nObjType;
+ var guid = guidSource.ToGuid();
- //set the Unique Id to the one we've generated
- Update.Table("cmsPropertyType").Set(new {uniqueID = ptGuid}).Where(new {id = data.ptId });
+ // set the Unique Id to the one we've generated
+ Update.Table("cmsPropertyType").Set(new { uniqueID = guid }).Where(new { id = data.ptId });
}
}
-
-
}
public override void Down()
- {
- }
+ { }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs
index cb0ad58a49..01db36abd9 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs
@@ -3,6 +3,7 @@ using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
@@ -118,6 +119,22 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
}
+ var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
+ //in some databases there's an index (IX_Master) on the master column which needs to be dropped first
+ var foundIndex = dbIndexes.FirstOrDefault(x => x.TableName.InvariantEquals("cmsTemplate") && x.ColumnName.InvariantEquals("master"));
+ if (foundIndex != null)
+ {
+ Delete.Index(foundIndex.IndexName).OnTable("cmsTemplate");
+ }
+
if (cols.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate")))
{
Delete.Column("master").FromTable("cmsTemplate");
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs
index 6598719454..32c81700a8 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs
@@ -31,58 +31,65 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne
//Fetch all PropertyTypes that belongs to a PropertyTypeGroup
//NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns
// won't exist yet
- var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0");
+ var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0");
- var propertyGroups = database.Fetch("WHERE id > 0");
+ // need to use dynamic, as PropertyTypeGroupDto has new properties
+ var propertyGroups = database.Fetch("SELECT * FROM cmsPropertyTypeGroup WHERE id > 0");
foreach (var propertyType in propertyTypes)
{
- //Get the PropertyTypeGroup that the current PropertyType references
- var parentPropertyTypeGroup = propertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyTypeGroupId);
- if (parentPropertyTypeGroup != null)
+ // get the PropertyTypeGroup of the current PropertyType, skip if not found
+ var propertyTypeGroup = propertyGroups.FirstOrDefault(x => x.id == propertyType.propertyTypeGroupId);
+ if (propertyTypeGroup == null) continue;
+
+ // if the PropretyTypeGroup belongs to the same content type as the PropertyType, then fine
+ if (propertyTypeGroup.contenttypeNodeId == propertyType.contentTypeId) continue;
+
+ // else we want to assign the PropertyType to a proper PropertyTypeGroup
+ // ie one that does belong to the same content - look for it
+ var okPropertyTypeGroup = propertyGroups.FirstOrDefault(x =>
+ x.text == propertyTypeGroup.text && // same name
+ x.contenttypeNodeId == propertyType.contentTypeId); // but for proper content type
+
+ if (okPropertyTypeGroup == null)
{
- //If the ContentType is the same on the PropertyType and the PropertyTypeGroup the group is valid and we skip to the next
- if (parentPropertyTypeGroup.ContentTypeNodeId == propertyType.ContentTypeId) continue;
-
- //Check if the 'new' PropertyTypeGroup has already been created
- var existingPropertyTypeGroup =
- propertyGroups.FirstOrDefault(
- x =>
- x.ParentGroupId == parentPropertyTypeGroup.Id && x.Text == parentPropertyTypeGroup.Text &&
- x.ContentTypeNodeId == propertyType.ContentTypeId);
-
- //This should ensure that we don't create duplicate groups for a single ContentType
- if (existingPropertyTypeGroup == null)
+ // does not exist, create a new PropertyTypeGroup
+ // cannot use a PropertyTypeGroupDto because of the new (not-yet-existing) uniqueID property
+ // cannot use a dynamic because database.Insert fails to set the value of property
+ var propertyGroup = new PropertyTypeGroupDtoTemp
{
+ id = 0,
+ contenttypeNodeId = propertyType.contentTypeId,
+ text = propertyTypeGroup.text,
+ sortorder = propertyTypeGroup.sortorder
+ };
- //Create a new PropertyTypeGroup that references the parent group that the PropertyType was referencing pre-6.0.1
- var propertyGroup = new PropertyTypeGroupDto
- {
- ContentTypeNodeId = propertyType.ContentTypeId,
- ParentGroupId = parentPropertyTypeGroup.Id,
- Text = parentPropertyTypeGroup.Text,
- SortOrder = parentPropertyTypeGroup.SortOrder
- };
+ // save + add to list of groups
+ int id = Convert.ToInt16(database.Insert("cmsPropertyTypeGroup", "id", propertyGroup));
+ propertyGroup.id = id;
+ propertyGroups.Add(propertyGroup);
- //Save the PropertyTypeGroup in the database and update the list of groups with this new group
- int id = Convert.ToInt16(database.Insert(propertyGroup));
- propertyGroup.Id = id;
- propertyGroups.Add(propertyGroup);
- //Update the reference to the new PropertyTypeGroup on the current PropertyType
- propertyType.PropertyTypeGroupId = id;
- database.Update(propertyType);
- }
- else
- {
- //Update the reference to the existing PropertyTypeGroup on the current PropertyType
- propertyType.PropertyTypeGroupId = existingPropertyTypeGroup.Id;
- database.Update(propertyType);
- }
+ // update the PropertyType to use the new PropertyTypeGroup
+ propertyType.propertyTypeGroupId = id;
}
+ else
+ {
+ // exists, update PropertyType to use the PropertyTypeGroup
+ propertyType.propertyTypeGroupId = okPropertyTypeGroup.id;
+ }
+ database.Update("cmsPropertyType", "id", propertyType);
}
}
return string.Empty;
}
+
+ private class PropertyTypeGroupDtoTemp
+ {
+ public int id { get; set; }
+ public int contenttypeNodeId { get; set; }
+ public string text { get; set; }
+ public int sortorder { get; set; }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs
index d55ad8fc14..88f90639d1 100644
--- a/src/Umbraco.Core/Persistence/PetaPoco.cs
+++ b/src/Umbraco.Core/Persistence/PetaPoco.cs
@@ -834,7 +834,7 @@ namespace Umbraco.Core.Persistence
var pd = PocoData.ForType(typeof(T));
try
{
- r = cmd.ExecuteReader();
+ r = cmd.ExecuteReaderWithRetry();
OnExecutedCommand(cmd);
}
catch (Exception x)
diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
index f3cbe5a583..4679b9b2ad 100644
--- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
+++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
@@ -128,6 +128,34 @@ namespace Umbraco.Core.Persistence.Querying
right = Visit(b.Right);
}
}
+ else if (operand == "=")
+ {
+ // deal with (x == true|false) - most common
+ var constRight = b.Right as ConstantExpression;
+ if (constRight != null && constRight.Type == typeof (bool))
+ return ((bool) constRight.Value) ? VisitNotNot(b.Left) : VisitNot(b.Left);
+ right = Visit(b.Right);
+
+ // deal with (true|false == x) - why not
+ var constLeft = b.Left as ConstantExpression;
+ if (constLeft != null && constLeft.Type == typeof (bool))
+ return ((bool) constLeft.Value) ? VisitNotNot(b.Right) : VisitNot(b.Right);
+ left = Visit(b.Left);
+ }
+ else if (operand == "<>")
+ {
+ // deal with (x != true|false) - most common
+ var constRight = b.Right as ConstantExpression;
+ if (constRight != null && constRight.Type == typeof(bool))
+ return ((bool) constRight.Value) ? VisitNot(b.Left) : VisitNotNot(b.Left);
+ right = Visit(b.Right);
+
+ // deal with (true|false != x) - why not
+ var constLeft = b.Left as ConstantExpression;
+ if (constLeft != null && constLeft.Type == typeof(bool))
+ return ((bool) constLeft.Value) ? VisitNot(b.Right) : VisitNotNot(b.Right);
+ left = Visit(b.Left);
+ }
else
{
left = Visit(b.Left);
@@ -152,7 +180,7 @@ namespace Umbraco.Core.Persistence.Querying
case "COALESCE":
return string.Format("{0}({1},{2})", operand, left, right);
default:
- return left + " " + operand + " " + right;
+ return "(" + left + " " + operand + " " + right + ")";
}
}
@@ -231,25 +259,47 @@ namespace Umbraco.Core.Persistence.Querying
switch (u.NodeType)
{
case ExpressionType.Not:
- var o = Visit(u.Operand);
-
- //use a Not equal operator instead of <> since we don't know that <> works in all sql servers
-
- switch (u.Operand.NodeType)
- {
- case ExpressionType.MemberAccess:
- //In this case it wil be a false property , i.e. x => !Trashed
- SqlParameters.Add(true);
- return string.Format("NOT ({0} = @0)", o);
- default:
- //In this case it could be anything else, such as: x => !x.Path.StartsWith("-20")
- return string.Format("NOT ({0})", o);
- }
+ return VisitNot(u.Operand);
default:
return Visit(u.Operand);
}
}
+ private string VisitNot(Expression exp)
+ {
+ var o = Visit(exp);
+
+ // use a "NOT (...)" syntax instead of "<>" since we don't know whether "<>" works in all sql servers
+ // also, x.StartsWith(...) translates to "x LIKE '...%'" which we cannot "<>" and have to "NOT (...")
+
+ switch (exp.NodeType)
+ {
+ case ExpressionType.MemberAccess:
+ // false property , i.e. x => !Trashed
+ SqlParameters.Add(true);
+ return string.Format("NOT ({0} = @{1})", o, SqlParameters.Count - 1);
+ default:
+ // could be anything else, such as: x => !x.Path.StartsWith("-20")
+ return "NOT (" + o + ")";
+ }
+ }
+
+ private string VisitNotNot(Expression exp)
+ {
+ var o = Visit(exp);
+
+ switch (exp.NodeType)
+ {
+ case ExpressionType.MemberAccess:
+ // true property, i.e. x => Trashed
+ SqlParameters.Add(true);
+ return string.Format("({0} = @{1})", o, SqlParameters.Count - 1);
+ default:
+ // could be anything else, such as: x => x.Path.StartsWith("-20")
+ return o;
+ }
+ }
+
protected virtual string VisitNewArray(NewArrayExpression na)
{
diff --git a/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs b/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs
index 859fa7cf5a..923348e729 100644
--- a/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs
+++ b/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs
@@ -18,8 +18,11 @@ namespace Umbraco.Core.Persistence.Relators
// Is this the same DictionaryItem as the current one we're processing
if (Current != null && Current.Id == a.Id)
{
- // Yes, just add this User2AppDto to the current item's collection
- Current.User2AppDtos.Add(p);
+ if (p.AppAlias.IsNullOrWhiteSpace() == false)
+ {
+ // Yes, just add this User2AppDto to the current item's collection
+ Current.User2AppDtos.Add(p);
+ }
// Return null to indicate we're not done with this User yet
return null;
@@ -35,7 +38,7 @@ namespace Umbraco.Core.Persistence.Relators
Current = a;
Current.User2AppDtos = new List();
//this can be null since we are doing a left join
- if (p.AppAlias != null)
+ if (p.AppAlias.IsNullOrWhiteSpace() == false)
{
Current.User2AppDtos.Add(p);
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 60d7d8e7ef..5f96cad114 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -105,6 +105,7 @@ namespace Umbraco.Core.Persistence.Repositories
#region Overrides of PetaPocoRepositoryBase
+
protected override Sql GetBaseQuery(bool isCount)
{
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
@@ -155,8 +156,8 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
"DELETE FROM cmsContent WHERE nodeId = @Id",
- "DELETE FROM umbracoNode WHERE id = @Id",
- "DELETE FROM umbracoAccess WHERE nodeId = @Id"
+ "DELETE FROM umbracoAccess WHERE nodeId = @Id",
+ "DELETE FROM umbracoNode WHERE id = @Id"
};
return list;
}
@@ -234,7 +235,14 @@ namespace Umbraco.Core.Persistence.Repositories
var processed = 0;
do
{
- var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
+ //NOTE: This is an important call, we cannot simply make a call to:
+ // GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
+ // because that method is used to query 'latest' content items where in this case we don't necessarily
+ // want latest content items because a pulished content item might not actually be the latest.
+ // see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982
+ var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total,
+ new Tuple("cmsDocument", "nodeId"),
+ ProcessQuery, "Path", Direction.Ascending, true);
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
@@ -682,8 +690,7 @@ namespace Umbraco.Core.Persistence.Repositories
public int CountPublished()
{
var sql = GetBaseQuery(true).Where(x => x.Trashed == false)
- .Where(x => x.Published == true)
- .Where(x => x.Newest == true);
+ .Where(x => x.Published == true);
return Database.ExecuteScalar(sql);
}
@@ -728,7 +735,7 @@ namespace Umbraco.Core.Persistence.Repositories
///
///
public void AddOrUpdateContentXml(IContent content, Func xml)
- {
+ {
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity(content, xml));
}
@@ -760,10 +767,11 @@ namespace Umbraco.Core.Persistence.Repositories
/// Total records query would return without paging
/// Field to order by
/// Direction to order by
+ /// Flag to indicate when ordering by system field
/// Search text filter
/// An Enumerable list of objects
public IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords,
- string orderBy, Direction orderDirection, string filter = "")
+ string orderBy, Direction orderDirection, bool orderBySystemField, string filter = "")
{
//NOTE: This uses the GetBaseQuery method but that does not take into account the required 'newest' field which is
@@ -782,7 +790,7 @@ namespace Umbraco.Core.Persistence.Repositories
return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords,
new Tuple("cmsDocument", "nodeId"),
- ProcessQuery, orderBy, orderDirection,
+ ProcessQuery, orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -826,11 +834,11 @@ namespace Umbraco.Core.Persistence.Repositories
var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray())
.ToArray();
-
+
var ids = dtos
.Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
.Select(x => x.TemplateId.Value).ToArray();
-
+
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
var templates = ids.Length == 0 ? Enumerable.Empty() : _templateRepository.GetAll(ids).ToArray();
@@ -972,4 +980,4 @@ namespace Umbraco.Core.Persistence.Repositories
_contentXmlRepository.Dispose();
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
index 631bb7e1fb..dc85ad3a33 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
@@ -6,6 +6,8 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Umbraco.Core.Events;
+using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -16,6 +18,7 @@ using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Relators;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -27,15 +30,60 @@ namespace Umbraco.Core.Persistence.Repositories
internal abstract class ContentTypeBaseRepository : PetaPocoRepositoryBase, IReadRepository
where TEntity : class, IContentTypeComposition
{
-
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
: base(work, cache, logger, sqlSyntax)
{
- _guidRepo = new GuidReadOnlyContentTypeBaseRepository(this, work, cache, logger, sqlSyntax);
}
- private readonly GuidReadOnlyContentTypeBaseRepository _guidRepo;
+ public IEnumerable> Move(TEntity toMove, EntityContainer container)
+ {
+ var parentId = Constants.System.Root;
+ if (container != null)
+ {
+ // Check on paths
+ if ((string.Format(",{0},", container.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1)
+ {
+ throw new DataOperationException(MoveOperationStatusType.FailedNotAllowedByPath);
+ }
+ parentId = container.Id;
+ }
+ //used to track all the moved entities to be given to the event
+ var moveInfo = new List>
+ {
+ new MoveEventInfo(toMove, toMove.Path, parentId)
+ };
+
+
+ // get the level delta (old pos to new pos)
+ var levelDelta = container == null
+ ? 1 - toMove.Level
+ : container.Level + 1 - toMove.Level;
+
+ // move to parent (or -1), update path, save
+ toMove.ParentId = parentId;
+ var toMovePath = toMove.Path + ","; // save before changing
+ toMove.Path = (container == null ? Constants.System.Root.ToString() : container.Path) + "," + toMove.Id;
+ toMove.Level = container == null ? 1 : container.Level + 1;
+ AddOrUpdate(toMove);
+
+ //update all descendants, update in order of level
+ var descendants = GetByQuery(new Query().Where(type => type.Path.StartsWith(toMovePath)));
+ var paths = new Dictionary();
+ paths[toMove.Id] = toMove.Path;
+
+ foreach (var descendant in descendants.OrderBy(x => x.Level))
+ {
+ moveInfo.Add(new MoveEventInfo(descendant, descendant.Path, descendant.ParentId));
+
+ descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id;
+ descendant.Level += levelDelta;
+
+ AddOrUpdate(descendant);
+ }
+
+ return moveInfo;
+ }
///
/// Returns the content type ids that match the query
///
@@ -45,15 +93,15 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sqlClause = new Sql();
sqlClause.Select("*")
- .From()
- .RightJoin()
- .On(left => left.Id, right => right.PropertyTypeGroupId)
- .InnerJoin()
- .On(left => left.DataTypeId, right => right.DataTypeId);
+ .From(SqlSyntax)
+ .RightJoin(SqlSyntax)
+ .On(SqlSyntax, left => left.Id, right => right.PropertyTypeGroupId)
+ .InnerJoin(SqlSyntax)
+ .On(SqlSyntax, left => left.DataTypeId, right => right.DataTypeId);
var translator = new SqlTranslator(sqlClause, query);
var sql = translator.Translate()
- .OrderBy(x => x.PropertyTypeGroupId);
+ .OrderBy(x => x.PropertyTypeGroupId, SqlSyntax);
var dtos = Database.Fetch(new GroupPropertyTypeRelator().Map, sql);
@@ -68,8 +116,11 @@ namespace Umbraco.Core.Persistence.Repositories
return new PropertyType(propertyEditorAlias, dbType, propertyTypeAlias);
}
- protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity)
+ protected void PersistNewBaseContentType(IContentTypeComposition entity)
{
+ var factory = new ContentTypeFactory();
+ var dto = factory.BuildContentTypeDto(entity);
+
//Cannot add a duplicate content type type
var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType
INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
@@ -181,38 +232,35 @@ AND umbracoNode.nodeObjectType = @objectType",
}
}
- protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeComposition entity)
+ protected void PersistUpdatedBaseContentType(IContentTypeComposition entity)
{
+ var factory = new ContentTypeFactory();
+ var dto = factory.BuildContentTypeDto(entity);
- //Cannot update to a duplicate alias
+ // ensure the alias is not used already
var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType
INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
WHERE cmsContentType." + SqlSyntax.GetQuotedColumnName("alias") + @"= @alias
AND umbracoNode.nodeObjectType = @objectType
AND umbracoNode.id <> @id",
- new { id = dto.NodeId, alias = entity.Alias, objectType = NodeObjectTypeId });
+ new { id = dto.NodeId, alias = dto.Alias, objectType = NodeObjectTypeId });
if (exists > 0)
- {
- throw new DuplicateNameException("An item with the alias " + entity.Alias + " already exists");
- }
-
- var propertyGroupFactory = new PropertyGroupFactory(entity.Id);
+ throw new DuplicateNameException("An item with the alias " + dto.Alias + " already exists");
+ // handle (update) the node
var nodeDto = dto.NodeDto;
- var o = Database.Update(nodeDto);
+ Database.Update(nodeDto);
+ // fixme - why? we are UPDATING so we should ALREADY have a PK!
//Look up ContentType entry to get PrimaryKey for updating the DTO
var dtoPk = Database.First("WHERE nodeId = @Id", new { Id = entity.Id });
dto.PrimaryKey = dtoPk.PrimaryKey;
Database.Update(dto);
- //Delete the ContentType composition entries before adding the updated collection
+ // handle (delete then recreate) compositions
Database.Delete("WHERE childContentTypeId = @Id", new { Id = entity.Id });
- //Update ContentType composition in new table
foreach (var composition in entity.ContentTypeComposition)
- {
Database.Insert(new ContentType2ContentTypeDto { ParentId = composition.Id, ChildId = entity.Id });
- }
//Removing a ContentType from a composition (U4-1690)
//1. Find content based on the current ContentType: entity.Id
@@ -237,7 +285,7 @@ AND umbracoNode.id <> @id",
{
//Find PropertyTypes for the removed ContentType
var propertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { Id = key });
- //Loop through the Content that is based on the current ContentType in order to remove the Properties that are
+ //Loop through the Content that is based on the current ContentType in order to remove the Properties that are
//based on the PropertyTypes that belong to the removed ContentType.
foreach (var contentDto in contentDtos)
{
@@ -291,54 +339,43 @@ AND umbracoNode.id <> @id",
}
}
- if (entity.IsPropertyDirty("PropertyGroups") ||
- entity.PropertyGroups.Any(x => x.IsDirty()))
+ if (entity.IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
{
- //Delete Tabs/Groups by excepting entries from db with entries from collections
- var dbPropertyGroups =
- Database.Fetch("WHERE contenttypeNodeId = @Id", new { Id = entity.Id })
- .Select(x => new Tuple(x.Id, x.Text))
- .ToList();
- var entityPropertyGroups = entity.PropertyGroups.Select(x => new Tuple(x.Id, x.Name)).ToList();
- var tabsToDelete = dbPropertyGroups.Select(x => x.Item1).Except(entityPropertyGroups.Select(x => x.Item1));
- var tabs = dbPropertyGroups.Where(x => tabsToDelete.Any(y => y == x.Item1));
- //Update Tab name downstream to ensure renaming is done properly
- foreach (var propertyGroup in entityPropertyGroups)
+ // todo
+ // we used to try to propagate tabs renaming downstream, relying on ParentId, but
+ // 1) ParentId makes no sense (if a tab can be inherited from multiple composition
+ // types) so we would need to figure things out differently, visiting downstream
+ // content types and looking for tabs with the same name...
+ // 2) It was not deployable as changing a content type changes other content types
+ // that was not deterministic, because it would depend on the order of the changes.
+ // That last point could be fixed if (1) is fixed, but then it still is an issue with
+ // deploy because changing a content type changes other content types that are not
+ // dependencies but dependents, and then what?
+ //
+ // So... for the time being, all renaming propagation is disabled. We just don't do it.
+
+ // (all gone)
+
+ // delete tabs that do not exist anymore
+ // get the tabs that are currently existing (in the db)
+ // get the tabs that we want, now
+ // and derive the tabs that we want to delete
+ var existingPropertyGroups = Database.Fetch("WHERE contentTypeNodeId = @id", new { id = entity.Id })
+ .Select(x => x.Id)
+ .ToList();
+ var newPropertyGroups = entity.PropertyGroups.Select(x => x.Id).ToList();
+ var tabsToDelete = existingPropertyGroups
+ .Except(newPropertyGroups)
+ .ToArray();
+
+ // move properties to generic properties, and delete the tabs
+ if (tabsToDelete.Length > 0)
{
- Database.Update("SET Text = @TabName WHERE parentGroupId = @TabId",
- new { TabName = propertyGroup.Item2, TabId = propertyGroup.Item1 });
-
- var childGroups = Database.Fetch("WHERE parentGroupId = @TabId", new { TabId = propertyGroup.Item1 });
- foreach (var childGroup in childGroups)
- {
- var sibling = Database.Fetch("WHERE contenttypeNodeId = @Id AND text = @Name",
- new { Id = childGroup.ContentTypeNodeId, Name = propertyGroup.Item2 })
- .FirstOrDefault(x => x.ParentGroupId.HasValue == false || x.ParentGroupId.Value.Equals(propertyGroup.Item1) == false);
- //If the child group doesn't have a sibling there is no chance of duplicates and we continue
- if (sibling == null || (sibling.ParentGroupId.HasValue && sibling.ParentGroupId.Value.Equals(propertyGroup.Item1))) continue;
-
- //Since the child group has a sibling with the same name we need to point any PropertyTypes to the sibling
- //as this child group is about to leave the party.
- Database.Update(
- "SET propertyTypeGroupId = @PropertyTypeGroupId WHERE propertyTypeGroupId = @PropertyGroupId AND ContentTypeId = @ContentTypeId",
- new { PropertyTypeGroupId = sibling.Id, PropertyGroupId = childGroup.Id, ContentTypeId = childGroup.ContentTypeNodeId });
-
- //Since the parent group has been renamed and we have duplicates we remove this group
- //and leave our sibling in charge of the part.
- Database.Delete(childGroup);
- }
- }
- //Do Tab updates
- foreach (var tab in tabs)
- {
- Database.Update("SET propertyTypeGroupId = NULL WHERE propertyTypeGroupId = @PropertyGroupId",
- new { PropertyGroupId = tab.Item1 });
- Database.Update("SET parentGroupId = NULL WHERE parentGroupId = @TabId",
- new { TabId = tab.Item1 });
- Database.Delete("WHERE contenttypeNodeId = @Id AND text = @Name",
- new { Id = entity.Id, Name = tab.Item2 });
+ Database.Update("SET propertyTypeGroupId=NULL WHERE propertyTypeGroupId IN (@ids)", new { ids = tabsToDelete });
+ Database.Delete("WHERE id IN (@ids)", new { ids = tabsToDelete });
}
}
+ var propertyGroupFactory = new PropertyGroupFactory(entity.Id);
//Run through all groups to insert or update entries
foreach (var propertyGroup in entity.PropertyGroups)
@@ -372,7 +409,7 @@ AND umbracoNode.id <> @id",
AssignDataTypeFromPropertyEditor(propertyType);
}
- //validate the alias!
+ //validate the alias!
ValidateAlias(propertyType);
var propertyTypeDto = propertyGroupFactory.BuildPropertyTypeDto(tabId, propertyType);
@@ -382,25 +419,6 @@ AND umbracoNode.id <> @id",
if (propertyType.HasIdentity == false)
propertyType.Id = typePrimaryKey; //Set Id on new PropertyType
}
-
- //If a Composition is removed we need to update/reset references to the PropertyGroups on that ContentType
- if (entity.IsPropertyDirty("ContentTypeComposition") &&
- compositionBase != null &&
- compositionBase.RemovedContentTypeKeyTracker != null &&
- compositionBase.RemovedContentTypeKeyTracker.Any())
- {
- foreach (var compositionId in compositionBase.RemovedContentTypeKeyTracker)
- {
- var dbPropertyGroups =
- Database.Fetch("WHERE contenttypeNodeId = @Id", new { Id = compositionId })
- .Select(x => x.Id);
- foreach (var propertyGroup in dbPropertyGroups)
- {
- Database.Update("SET parentGroupId = NULL WHERE parentGroupId = @TabId AND contenttypeNodeId = @ContentTypeNodeId",
- new { TabId = propertyGroup, ContentTypeNodeId = entity.Id });
- }
- }
- }
}
protected IEnumerable GetAllowedContentTypeIds(int id)
@@ -454,6 +472,7 @@ AND umbracoNode.id <> @id",
propType.DataTypeDefinitionId = dto.DataTypeId;
propType.Description = dto.Description;
propType.Id = dto.Id;
+ propType.Key = dto.UniqueId;
propType.Name = dto.Name;
propType.Mandatory = dto.Mandatory;
propType.SortOrder = dto.SortOrder;
@@ -564,14 +583,13 @@ AND umbracoNode.id <> @id",
}
}
- public static IEnumerable GetMediaTypes(
- TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
+ public static IEnumerable GetMediaTypes(
+ Database db, ISqlSyntaxProvider sqlSyntax,
TRepo contentTypeRepository)
- where TRepo : IReadRepository
- where TId: struct
+ where TRepo : IReadRepository
{
- IDictionary> allParentMediaTypeIds;
- var mediaTypes = MapMediaTypes(mediaTypeIds, db, sqlSyntax, out allParentMediaTypeIds)
+ IDictionary> allParentMediaTypeIds;
+ var mediaTypes = MapMediaTypes(db, sqlSyntax, out allParentMediaTypeIds)
.ToArray();
MapContentTypeChildren(mediaTypes, db, sqlSyntax, contentTypeRepository, allParentMediaTypeIds);
@@ -579,16 +597,15 @@ AND umbracoNode.id <> @id",
return mediaTypes;
}
- public static IEnumerable GetContentTypes(
- TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
+ public static IEnumerable GetContentTypes(
+ Database db, ISqlSyntaxProvider sqlSyntax,
TRepo contentTypeRepository,
ITemplateRepository templateRepository)
- where TRepo : IReadRepository
- where TId : struct
+ where TRepo : IReadRepository
{
- IDictionary