Compare commits

..

1 Commits

Author SHA1 Message Date
Stephan d0ab80bfde Support clearing all datatype cache 2019-02-08 11:40:49 +01:00
766 changed files with 10816 additions and 8827 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/dev-v8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
+2 -4
View File
@@ -1,9 +1,7 @@
[![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=dev-v8)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
[![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS/tree/dev-v7) to go to the v7 branch._
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS) to go to the v7 branch._
_Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md)._
+1 -1
View File
@@ -5,7 +5,7 @@
* [Visual Studio 2017 Community (Free)](https://www.visualstudio.com/vs/community/), or Professional, Enterprise, etc... _(**Minimum of Version 15.7** or higher, this is important, you WILL get issues with lesser versions)_
* .NET Framework 4.7.2 installed, get it here: https://www.microsoft.com/net/download/thank-you/net472?survey=false
* .NET Framework 4.7.2 developer pack, get it here: https://www.microsoft.com/net/download/thank-you/net472-developer-pack _(be sure this is the ENU file which will be named `NDP472-DevPack-ENU.exe`)_
* Clone the Umbraco repository using the `dev-v8` branch. If your git client doesn't support specifying the branch as you clone then use the command `git clone --single-branch -b dev-v8 <your fork url>`. _(If you clone the repo using the default v7 branch and then checkout the `dev-v8` branch you **might** get problems)_
* Clone the Umbraco repository using the `temp8` branch. If your git client doesn't support specifying the branch as you clone then use the command `git clone --single-branch -b temp8 <your fork url>`. _(If you clone the repo using the default v7 branch and then checkout the `temp8` branch you **might** get problems)_
### Start the solution
+158 -161
View File
@@ -1,163 +1,160 @@
#
# Umbraco Cms Git Ignore
#
# common files
*.obj
*.pdb
*.user
*.aps
*.pch
*.vspscc
*.orig
*.suo
*.vs10x
*.ndproj
#
# Umbraco Cms Git Ignore
#
# common files
*.obj
*.pdb
*.user
*.aps
*.pch
*.vspscc
*.orig
*.suo
*.vs10x
*.ndproj
*.ignorer.*
# common directories
.DS_Store
._.DS_Store
.vs/
/local/
# build directories
[Bb]in/
[Db]ebug*/
[Rr]elease*/
obj/
# tools
_ReSharper*/
_NCrunch_*/
*.ncrunchsolution
*.ncrunchsolution.user
*.ncrunchproject
*.crunchsolution.cache
tools/NDepend/
[Tt]est[Rr]esult*
[Bb]uild[Ll]og.*
*.[Pp]ublish.xml
[sS]ource
[sS]andbox
umbraco.config
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
src/Umbraco.Tests/config/applications.config
src/Umbraco.Tests/config/trees.config
src/Umbraco.Web.UI/web.config
src/Umbraco.Web.UI/[Cc]onfig/ClientDependency.config
src/Umbraco.Web.UI/[Cc]onfig/serilog.config
src/Umbraco.Web.UI/[Cc]onfig/serilog.user.config
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
node_modules
lib-bower
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/*.loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/init.js
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/canvasdesigner.*.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/
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
src/Umbraco.Web.UI.Client/vwd.webinfo
src/Umbraco.Web.UI/App_Plugins/*
src/*.psess
src/*.vspx
NDependOut/*
QueryResult.htm
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
src/Umbraco.Web.UI/[Uu]mbraco/plugins/*
build/ApiDocs/*
build/ApiDocs/Output/*
src/Umbraco.Web.UI.Client/bower_components/*
/src/Umbraco.Web.UI/[Uu]mbraco/preview
/src/Umbraco.Web.UI/[Uu]mbraco/preview.old
# ignore rule for clearing out Belle (avoid rebuilding all the time)
preserve.belle
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
src/*.boltdata/
src/umbraco.sln.ide/*
src/.vs/
src/Umbraco.Web.UI/[Jj]s/*
src/Umbraco.Tests/[Mm]edia
tools/docfx/*
apidocs/_site/*
src/*/project.lock.json
src/.idea/*
apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
src/packages/
src/PrecompiledWeb/*
# build
build.out/
build.tmp/
build/hooks/
build/temp/
# common directories
.DS_Store
._.DS_Store
.vs/
/local/
# build directories
[Bb]in/
[Db]ebug*/
[Rr]elease*/
obj/
# tools
_ReSharper*/
_NCrunch_*/
*.ncrunchsolution
*.ncrunchsolution.user
*.ncrunchproject
*.crunchsolution.cache
tools/NDepend/
[Tt]est[Rr]esult*
[Bb]uild[Ll]og.*
*.[Pp]ublish.xml
[sS]ource
[sS]andbox
umbraco.config
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
src/Umbraco.Tests/config/applications.config
src/Umbraco.Tests/config/trees.config
src/Umbraco.Web.UI/web.config
src/Umbraco.Web.UI/[Cc]onfig/ClientDependency.config
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
node_modules
lib-bower
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/*.loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/init.js
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/canvasdesigner.*.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/
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
src/Umbraco.Web.UI.Client/vwd.webinfo
src/Umbraco.Web.UI/App_Plugins/*
src/*.psess
src/*.vspx
NDependOut/*
QueryResult.htm
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
src/Umbraco.Web.UI/[Uu]mbraco/plugins/*
build/ApiDocs/*
build/ApiDocs/Output/*
src/Umbraco.Web.UI.Client/bower_components/*
/src/Umbraco.Web.UI/[Uu]mbraco/preview
/src/Umbraco.Web.UI/[Uu]mbraco/preview.old
# ignore rule for clearing out Belle (avoid rebuilding all the time)
preserve.belle
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
src/*.boltdata/
src/umbraco.sln.ide/*
src/.vs/
src/Umbraco.Web.UI/[Jj]s/*
src/Umbraco.Tests/[Mm]edia
tools/docfx/*
apidocs/_site/*
src/*/project.lock.json
src/.idea/*
apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
src/packages/
src/PrecompiledWeb/*
# build
build.out/
build.tmp/
build/hooks/
build/temp/
# eof
/src/Umbraco.Web.UI.Client/TESTS-*.xml
+2 -2
View File
@@ -27,9 +27,9 @@
<dependency id="ClientDependency" version="[1.9.7,1.999999)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0,1.999999)" />
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
<dependency id="Examine" version="[1.0.1,1.999999)" />
<dependency id="Examine" version="[1.0.0-beta078,1.999999)" />
<dependency id="HtmlAgilityPack" version="[1.8.14,1.999999)" />
<dependency id="ImageProcessor" version="[2.7.0.100,2.999999)" />
<dependency id="ImageProcessor" version="[2.6.2.25,2.999999)" />
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />
<dependency id="LightInject.WebApi" version="[2.0.0,2.999999)" />
<dependency id="Markdown" version="[2.2.1,2.999999)" />
+4 -4
View File
@@ -25,15 +25,15 @@
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.4.0, 2.999999)" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.1]" />
<dependency id="ImageProcessor.Web" version="[4.10.0.100,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.5.0.100,2.999999)" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.0-alpha.37]" />
<dependency id="ImageProcessor.Web" version="[4.9.3.25,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.4.1.19,2.999999)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.1,2.999999)" />
<dependency id="Microsoft.Net.Compilers" version="[2.10.0,2.999999)" />
</group>
</dependencies>
</metadata>
<files>
+36 -46
View File
@@ -126,55 +126,45 @@
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.CodeAnalysis.CSharp')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='log4net')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Data.SqlServerCe')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.ValueTuple')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
</assemblyBinding>
</runtime>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.3.0" newVersion="1.2.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.1.0" newVersion="1.2.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.web.webPages.razor xdt:Transform="Remove" />
+2 -2
View File
@@ -18,5 +18,5 @@ using System.Resources;
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.1.0")]
[assembly: AssemblyInformationalVersion("8.1.0")]
[assembly: AssemblyFileVersion("8.0.0")]
[assembly: AssemblyInformationalVersion("8.0.0-alpha.58")]
+7 -13
View File
@@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
namespace Umbraco.Core.Cache
@@ -111,7 +110,7 @@ namespace Umbraco.Core.Cache
#region Lock
private const string ContextItemsLockKey = "Umbraco.Core.Cache.HttpRequestCache::LockEntered";
private bool _entered;
protected override void EnterReadLock() => EnterWriteLock();
@@ -119,13 +118,7 @@ namespace Umbraco.Core.Cache
{
if (HasContextItems)
{
// note: cannot keep 'entered' as a class variable here,
// since there is one per request - so storing it within
// ContextItems - which is locked, so this should be safe
var entered = false;
Monitor.Enter(ContextItems.SyncRoot, ref entered);
ContextItems[ContextItemsLockKey] = entered;
System.Threading.Monitor.Enter(ContextItems.SyncRoot, ref _entered);
}
}
@@ -133,10 +126,11 @@ namespace Umbraco.Core.Cache
protected override void ExitWriteLock()
{
var entered = (bool?) ContextItems[ContextItemsLockKey] ?? false;
if (entered)
Monitor.Exit(ContextItems.SyncRoot);
ContextItems.Remove(ContextItemsLockKey);
if (_entered)
{
_entered = false;
System.Threading.Monitor.Exit(ContextItems.SyncRoot);
}
}
#endregion
@@ -3,7 +3,6 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using Umbraco.Core.Composing;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -11,7 +10,7 @@ using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class AuditEventsComponent : IComponent
{
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class AuditEventsComposer : ComponentComposer<AuditEventsComponent>, ICoreComposer
{ }
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents the collection of <see cref="IComponent"/> implementations.
@@ -23,7 +24,7 @@ namespace Umbraco.Core.Composing
{
using (_logger.DebugDuration<ComponentCollection>($"Initializing. (log components when >{LogThresholdMilliseconds}ms)", "Initialized."))
{
foreach (var component in this)
foreach (var component in this.Reverse()) // terminate components in reverse order
{
var componentType = component.GetType();
using (_logger.DebugDuration<ComponentCollection>($"Initializing {componentType.FullName}.", $"Initialized {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Builds a <see cref="ComponentCollection"/>.
@@ -30,7 +31,7 @@ namespace Umbraco.Core.Composing
protected override IComponent CreateItem(IFactory factory, Type itemType)
{
using (_logger.DebugDuration<ComponentCollectionBuilder>($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
using (_logger.DebugDuration<Composers>($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
{
return base.CreateItem(factory, itemType);
}
@@ -1,6 +1,4 @@
using Umbraco.Core.Compose;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Provides a base class for composers which compose a component.
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer requires another composer.
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a component is required by another composer.
@@ -6,7 +6,7 @@ using System.Text;
using Umbraco.Core.Collections;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
// note: this class is NOT thread-safe in any ways
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a composition.
@@ -1,23 +1,64 @@
using System;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Logging.Viewer;
using Umbraco.Core.Migrations;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PackageActions;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Core.Logging.Viewer;
using Umbraco.Core.PackageActions;
namespace Umbraco.Core
namespace Umbraco.Core.Components
{
/// <summary>
/// Provides extension methods to the <see cref="Composition"/> class.
/// </summary>
public static partial class CompositionExtensions
public static class CompositionExtensions
{
#region FileSystems
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <typeparam name="TImplementing">The implementing type.</typeparam>
/// <param name="composition">The composition.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem, TImplementing>(this Composition composition)
where TImplementing : FileSystemWrapper, TFileSystem
where TFileSystem : class
{
composition.RegisterUnique<TFileSystem>(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
var supporting = factory.GetInstance<SupportingFileSystems>();
return fileSystems.GetFileSystem<TImplementing>(supporting.For<TFileSystem>());
});
}
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <param name="composition">The composition.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem>(this Composition composition)
where TFileSystem : FileSystemWrapper
{
composition.RegisterUnique(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
var supporting = factory.GetInstance<SupportingFileSystems>();
return fileSystems.GetFileSystem<TFileSystem>(supporting.For<TFileSystem>());
});
}
#endregion
#region Collection Builders
/// <summary>
@@ -69,6 +110,13 @@ namespace Umbraco.Core
internal static ManifestValueValidatorCollectionBuilder Validators(this Composition composition)
=> composition.WithCollectionBuilder<ManifestValueValidatorCollectionBuilder>();
/// <summary>
/// Gets the post-migrations collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
internal static PostMigrationCollectionBuilder PostMigrations(this Composition composition)
=> composition.WithCollectionBuilder<PostMigrationCollectionBuilder>();
/// <summary>
/// Gets the components collection builder.
/// </summary>
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer should be disabled.
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer should be disabled.
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer should be enabled.
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer should be enabled.
@@ -1,4 +1,4 @@
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a component.
@@ -1,4 +1,6 @@
namespace Umbraco.Core.Composing
using Umbraco.Core.Composing;
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a composer.
@@ -1,4 +1,4 @@
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a core <see cref="IComposer"/>.
@@ -1,4 +1,4 @@
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a runtime <see cref="IComposer"/>.
@@ -1,4 +1,4 @@
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a user <see cref="IComposer"/>.
@@ -1,10 +1,9 @@
using System.IO;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class ManifestWatcherComponent : IComponent
{
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ManifestWatcherComposer : ComponentComposer<ManifestWatcherComponent>, ICoreComposer
@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
// TODO: This should just exist in the content service/repo!
public sealed class RelateOnCopyComponent : IComponent
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public sealed class RelateOnCopyComposer : ComponentComposer<RelateOnCopyComponent>, ICoreComposer
@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class RelateOnTrashComponent : IComponent
{
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public sealed class RelateOnTrashComposer : ComponentComposer<RelateOnTrashComponent>, ICoreComposer
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Marks a composer to indicate a minimum and/or maximum runtime level for which the composer would compose.
@@ -1,11 +1,13 @@
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Components;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Core.Composing.Composers
{
/// <summary>
/// Compose configurations.
/// </summary>
public static class Configuration
public static class ConfigurationComposer
{
public static Composition ComposeConfiguration(this Composition composition)
{
@@ -1,10 +1,11 @@
using AutoMapper;
using Umbraco.Core.Components;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Core.Composing.Composers
{
public static class CoreMappingProfiles
public static class CoreMappingProfilesComposer
{
public static Composition ComposeCoreMappingProfiles(this Composition composition)
{
@@ -1,10 +1,10 @@
using Umbraco.Core.Compose;
using Umbraco.Core.Components;
using Umbraco.Core.IO;
using Umbraco.Core.IO.MediaPathSchemes;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Core.Composing.Composers
{
public static class FileSystems
public static class FileSystemsComposer
{
/*
* HOW TO REPLACE THE MEDIA UNDERLYING FILESYSTEM
@@ -73,13 +73,13 @@ namespace Umbraco.Core.Composing.CompositionExtensions
// it needs to be registered (not only the interface) because it provides additional
// functionality eg for scoping, and is injected in the scope provider - whereas the
// interface is really for end-users to get access to filesystems.
composition.RegisterUnique(factory => factory.CreateInstance<IO.FileSystems>(factory));
composition.RegisterUnique(factory => factory.CreateInstance<FileSystems>(factory));
// register IFileSystems, which gives access too all filesystems
composition.RegisterUnique<IFileSystems>(factory => factory.GetInstance<IO.FileSystems>());
composition.RegisterUnique<IFileSystems>(factory => factory.GetInstance<FileSystems>());
// register the scheme for media paths
composition.RegisterUnique<IMediaPathScheme, UniqueMediaPathScheme>();
composition.RegisterUnique<IMediaPathScheme, TwoGuidsMediaPathScheme>();
// register the IMediaFileSystem implementation
composition.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>();
@@ -1,12 +1,13 @@
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Components;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Core.Composing.Composers
{
/// <summary>
/// Composes repositories.
/// </summary>
public static class Repositories
public static class RepositoriesComposer
{
public static Composition ComposeRepositories(this Composition composition)
{
@@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Components;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
@@ -9,9 +10,9 @@ using Umbraco.Core.Packaging;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Core.Composing.Composers
{
public static class Services
public static class ServicesComposer
{
public static Composition ComposeServices(this Composition composition)
{
@@ -1,12 +1,43 @@
using Umbraco.Core.Composing;
using Umbraco.Core.Cache;
using Umbraco.Core.Components;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
namespace Umbraco.Core
namespace Umbraco.Core.Composing
{
/// <summary>
/// Provides extension methods to the <see cref="Composition"/> class.
/// </summary>
public static partial class CompositionExtensions
public static class CompositionExtensions
{
#region Essentials
/// <summary>
/// Registers essential services.
/// </summary>
public static void RegisterEssentials(this Composition composition,
ILogger logger, IProfiler profiler, IProfilingLogger profilingLogger,
IMainDom mainDom,
AppCaches appCaches,
IUmbracoDatabaseFactory databaseFactory,
TypeLoader typeLoader,
IRuntimeState state)
{
composition.RegisterUnique(logger);
composition.RegisterUnique(profiler);
composition.RegisterUnique(profilingLogger);
composition.RegisterUnique(mainDom);
composition.RegisterUnique(appCaches);
composition.RegisterUnique(databaseFactory);
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoDatabaseFactory>().SqlContext);
composition.RegisterUnique(typeLoader);
composition.RegisterUnique(state);
}
#endregion
#region Unique
/// <summary>
/// Registers a unique service as its own implementation.
/// </summary>
@@ -31,5 +62,7 @@ namespace Umbraco.Core
/// </summary>
public static void RegisterUnique<TService>(this Composition composition, TService instance)
=> composition.RegisterUnique(typeof(TService), instance);
#endregion
}
}
}
@@ -1,9 +1,8 @@
using System;
using System.Linq;
using System.Reflection;
using Umbraco.Core.Composing;
namespace Umbraco.Core
namespace Umbraco.Core.Composing
{
/// <summary>
/// Provides extension methods to the <see cref="IFactory"/> class.
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core
namespace Umbraco.Core.Composing
{
/// <summary>
/// Provides extension methods to the <see cref="IRegister"/> class.
+26 -8
View File
@@ -42,30 +42,30 @@ namespace Umbraco.Core.Composing
private string _currentAssembliesHash;
private IEnumerable<Assembly> _assemblies;
private bool _reportedChange;
private static string _localTempPath;
private static LocalTempStorage _localTempStorage;
private static string _fileBasePath;
/// <summary>
/// Initializes a new instance of the <see cref="TypeLoader"/> class.
/// </summary>
/// <param name="runtimeCache">The application runtime cache.</param>
/// <param name="localTempPath">Files storage location.</param>
/// <param name="localTempStorage">Files storage mode.</param>
/// <param name="logger">A profiling logger.</param>
public TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger)
: this(runtimeCache, localTempPath, logger, true)
public TypeLoader(IAppPolicyCache runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger)
: this(runtimeCache, localTempStorage, logger, true)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="TypeLoader"/> class.
/// </summary>
/// <param name="runtimeCache">The application runtime cache.</param>
/// <param name="localTempPath">Files storage location.</param>
/// <param name="localTempStorage">Files storage mode.</param>
/// <param name="logger">A profiling logger.</param>
/// <param name="detectChanges">Whether to detect changes using hashes.</param>
internal TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger, bool detectChanges)
internal TypeLoader(IAppPolicyCache runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger, bool detectChanges)
{
_runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
_localTempPath = localTempPath;
_localTempStorage = localTempStorage == LocalTempStorage.Unknown ? LocalTempStorage.Default : localTempStorage;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
if (detectChanges)
@@ -388,7 +388,25 @@ namespace Umbraco.Core.Composing
if (_fileBasePath != null)
return _fileBasePath;
_fileBasePath = Path.Combine(_localTempPath, "TypesCache", "umbraco-types." + NetworkHelper.FileSafeMachineName);
switch (_localTempStorage)
{
case LocalTempStorage.AspNetTemp:
_fileBasePath = Path.Combine(HttpRuntime.CodegenDir, "UmbracoData", "umbraco-types");
break;
case LocalTempStorage.EnvironmentTemp:
// include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path - assuming we cannot have SHA1 collisions on AppDomainAppId
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", appDomainHash);
_fileBasePath = Path.Combine(cachePath, "umbraco-types");
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache");
_fileBasePath = Path.Combine(tempFolder, "umbraco-types." + NetworkHelper.FileSafeMachineName);
break;
}
// ensure that the folder exists
var directory = Path.GetDirectoryName(_fileBasePath);
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.PackageActions;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core
namespace Umbraco.Core.Composing
{
internal static class TypeLoaderExtensions
{
@@ -1,35 +0,0 @@
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
namespace Umbraco.Core
{
/// <summary>
/// Provides extension methods to the <see cref="Composition"/> class.
/// </summary>
public static partial class CompositionExtensions
{
/// <summary>
/// Registers essential services.
/// </summary>
public static void RegisterEssentials(this Composition composition,
ILogger logger, IProfiler profiler, IProfilingLogger profilingLogger,
IMainDom mainDom,
AppCaches appCaches,
IUmbracoDatabaseFactory databaseFactory,
TypeLoader typeLoader,
IRuntimeState state)
{
composition.RegisterUnique(logger);
composition.RegisterUnique(profiler);
composition.RegisterUnique(profilingLogger);
composition.RegisterUnique(mainDom);
composition.RegisterUnique(appCaches);
composition.RegisterUnique(databaseFactory);
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoDatabaseFactory>().SqlContext);
composition.RegisterUnique(typeLoader);
composition.RegisterUnique(state);
}
}
}
@@ -1,47 +0,0 @@
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
namespace Umbraco.Core
{
/// <summary>
/// Provides extension methods to the <see cref="Composition"/> class.
/// </summary>
public static partial class CompositionExtensions
{
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <typeparam name="TImplementing">The implementing type.</typeparam>
/// <param name="composition">The composition.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem, TImplementing>(this Composition composition)
where TImplementing : FileSystemWrapper, TFileSystem
where TFileSystem : class
{
composition.RegisterUnique<TFileSystem>(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
var supporting = factory.GetInstance<SupportingFileSystems>();
return fileSystems.GetFileSystem<TImplementing>(supporting.For<TFileSystem>());
});
}
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <param name="composition">The composition.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem>(this Composition composition)
where TFileSystem : FileSystemWrapper
{
composition.RegisterUnique(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
var supporting = factory.GetInstance<SupportingFileSystems>();
return fileSystems.GetFileSystem<TFileSystem>(supporting.For<TFileSystem>());
});
}
}
}
@@ -4,8 +4,15 @@ using System.Linq;
using System.Net.Configuration;
using System.Web;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Security;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Security;
namespace Umbraco.Core.Configuration
{
@@ -270,7 +277,12 @@ namespace Umbraco.Core.Configuration
}
}
/// <inheritdoc />
/// <summary>
/// This is the location type to store temporary files such as cache files or other localized files for a given machine
/// </summary>
/// <remarks>
/// Currently used for the xml cache file and the plugin cache files
/// </remarks>
public LocalTempStorage LocalTempStorageLocation
{
get
@@ -283,30 +295,6 @@ namespace Umbraco.Core.Configuration
}
}
/// <inheritdoc />
public string LocalTempPath
{
get
{
switch (LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
return System.IO.Path.Combine(HttpRuntime.CodegenDir, "UmbracoData");
case LocalTempStorage.EnvironmentTemp:
// include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path - assuming we cannot have SHA1 collisions on AppDomainAppId
var appDomainHash = HttpRuntime.AppDomainAppId.GenerateHash();
return System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", appDomainHash);
//case LocalTempStorage.Default:
//case LocalTempStorage.Unknown:
default:
return IOHelper.MapPath("~/App_Data/TEMP");
}
}
}
/// <summary>
/// Gets the default UI language.
/// </summary>
@@ -360,5 +348,10 @@ namespace Umbraco.Core.Configuration
}
}
}
}
}
@@ -128,7 +128,6 @@ namespace Umbraco.Core.Configuration
if (routes == null) throw new ArgumentNullException(nameof(routes));
//check if the current request matches a route, if so then it is reserved.
//TODO: This value should be cached! Else this is doing double routing in MVC every request!
var route = routes.GetRouteData(httpContext);
if (route != null)
return true;
@@ -1,4 +1,6 @@
namespace Umbraco.Core.Configuration
using System;
namespace Umbraco.Core.Configuration
{
/// <summary>
/// Contains general settings information for the entire Umbraco instance based on information from web.config appsettings
@@ -59,13 +61,11 @@
int VersionCheckPeriod { get; }
/// <summary>
/// Gets the configuration for the location of temporary files.
/// This is the location type to store temporary files such as cache files or other localized files for a given machine
/// </summary>
/// <remarks>
/// Used for some cache files and for specific environments such as Azure
/// </remarks>
LocalTempStorage LocalTempStorageLocation { get; }
/// <summary>
/// Gets the location of temporary files.
/// </summary>
string LocalTempPath { get; }
}
}
+3 -3
View File
@@ -77,7 +77,7 @@
/// alias for the macro tree.
/// </summary>
public const string Macros = "macros";
/// <summary>
/// alias for the datatype tree.
/// </summary>
@@ -92,7 +92,7 @@
/// alias for the dictionary tree.
/// </summary>
public const string Dictionary = "dictionary";
public const string Stylesheets = "stylesheets";
/// <summary>
@@ -121,7 +121,7 @@
public const string Templates = "templates";
public const string RelationTypes = "relationTypes";
public const string Languages = "languages";
/// <summary>
+6 -6
View File
@@ -219,7 +219,7 @@ namespace Umbraco.Core
},
{
FailedPasswordAttempts,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts)
new PropertyType(PropertyEditors.Aliases.NoEdit, ValueStorageType.Integer, true, FailedPasswordAttempts)
{
Name = FailedPasswordAttemptsLabel
}
@@ -240,35 +240,35 @@ namespace Umbraco.Core
},
{
LastLockoutDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
new PropertyType(PropertyEditors.Aliases.NoEdit, ValueStorageType.Date, true, LastLockoutDate)
{
Name = LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
new PropertyType(PropertyEditors.Aliases.NoEdit, ValueStorageType.Date, true, LastLoginDate)
{
Name = LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
new PropertyType(PropertyEditors.Aliases.NoEdit, ValueStorageType.Date, true, LastPasswordChangeDate)
{
Name = LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
new PropertyType(PropertyEditors.Aliases.NoEdit, ValueStorageType.Nvarchar, true, PasswordAnswer)
{
Name = PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
new PropertyType(PropertyEditors.Aliases.NoEdit, ValueStorageType.Nvarchar, true, PasswordQuestion)
{
Name = PasswordQuestionLabel
}
+11 -7
View File
@@ -34,7 +34,11 @@ namespace Umbraco.Core
/// </summary>
public const string ContentPicker = "Umbraco.ContentPicker";
/// <summary>
/// Date.
/// </summary>
public const string Date = "Umbraco.Date";
/// <summary>
/// DateTime.
/// </summary>
@@ -44,7 +48,7 @@ namespace Umbraco.Core
/// DropDown List.
/// </summary>
public const string DropDownListFlexible = "Umbraco.DropDown.Flexible";
/// <summary>
/// Grid.
/// </summary>
@@ -101,9 +105,9 @@ namespace Umbraco.Core
public const string MultipleTextstring = "Umbraco.MultipleTextstring";
/// <summary>
/// Label.
/// NoEdit.
/// </summary>
public const string Label = "Umbraco.Label";
public const string NoEdit = "Umbraco.NoEdit";
/// <summary>
/// Picker Relations.
@@ -114,7 +118,7 @@ namespace Umbraco.Core
/// RadioButton list.
/// </summary>
public const string RadioButtonList = "Umbraco.RadioButtonList";
/// <summary>
/// Slider.
/// </summary>
@@ -138,7 +142,7 @@ namespace Umbraco.Core
/// <summary>
/// TinyMCE
/// </summary>
public const string TinyMce = "Umbraco.TinyMCE";
public const string TinyMce = "Umbraco.TinyMCEv3";
/// <summary>
/// Boolean.
@@ -159,7 +163,7 @@ namespace Umbraco.Core
/// Upload Field.
/// </summary>
public const string UploadField = "Umbraco.UploadField";
/// <summary>
/// Email Address.
/// </summary>
+64
View File
@@ -0,0 +1,64 @@
using System;
namespace Umbraco.Core
{
/// <summary>
/// Abstract implementation of IDisposable.
/// </summary>
/// <remarks>
/// This is for objects that DO have unmanaged resources. Use <see cref="DisposableObjectSlim"/>
/// for objects that do NOT have unmanaged resources, and avoid creating a finalizer.
///
/// Can also be used as a pattern for when inheriting is not possible.
///
/// See also: https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspx
/// See also: https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/
///
/// Note: if an object's ctor throws, it will never be disposed, and so if that ctor
/// has allocated disposable objects, it should take care of disposing them.
/// </remarks>
public abstract class DisposableObject : IDisposable
{
private readonly object _locko = new object();
// gets a value indicating whether this instance is disposed.
// for internal tests only (not thread safe)
public bool Disposed { get; private set; }
// implements IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// finalizer
~DisposableObject()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
// can happen if the object construction failed
if (_locko == null)
return;
lock (_locko)
{
if (Disposed) return;
Disposed = true;
}
DisposeUnmanagedResources();
if (disposing)
DisposeResources();
}
protected abstract void DisposeResources();
protected virtual void DisposeUnmanagedResources()
{ }
}
}
-67
View File
@@ -40,72 +40,5 @@ namespace Umbraco.Core
public DecomposedGuid(Guid value) : this() => this.Value = value;
}
private static readonly char[] Base32Table =
{
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5'
};
/// <summary>
/// Converts a Guid into a base-32 string.
/// </summary>
/// <param name="guid">A Guid.</param>
/// <param name="length">The string length.</param>
/// <returns>A base-32 encoded string.</returns>
/// <remarks>
/// <para>A base-32 string representation of a Guid is the shortest, efficient, representation
/// that is case insensitive (base-64 is case sensitive).</para>
/// <para>Length must be 1-26, anything else becomes 26.</para>
/// </remarks>
public static string ToBase32String(Guid guid, int length = 26)
{
if (length <= 0 || length > 26)
length = 26;
var bytes = guid.ToByteArray(); // a Guid is 128 bits ie 16 bytes
// this could be optimized by making it unsafe,
// and fixing the table + bytes + chars (see Convert.ToBase64CharArray)
// each block of 5 bytes = 5*8 = 40 bits
// becomes 40 bits = 8*5 = 8 byte-32 chars
// a Guid is 3 blocks + 8 bits
// so it turns into a 3*8+2 = 26 chars string
var chars = new char[length];
var i = 0;
var j = 0;
while (i < 15)
{
if (j == length) break;
chars[j++] = Base32Table[(bytes[i] & 0b1111_1000) >> 3];
if (j == length) break;
chars[j++] = Base32Table[((bytes[i] & 0b0000_0111) << 2) | ((bytes[i + 1] & 0b1100_0000) >> 6)];
if (j == length) break;
chars[j++] = Base32Table[(bytes[i + 1] & 0b0011_1110) >> 1];
if (j == length) break;
chars[j++] = Base32Table[(bytes[i + 1] & 0b0000_0001) | ((bytes[i + 2] & 0b1111_0000) >> 4)];
if (j == length) break;
chars[j++] = Base32Table[((bytes[i + 2] & 0b0000_1111) << 1) | ((bytes[i + 3] & 0b1000_0000) >> 7)];
if (j == length) break;
chars[j++] = Base32Table[(bytes[i + 3] & 0b0111_1100) >> 2];
if (j == length) break;
chars[j++] = Base32Table[((bytes[i + 3] & 0b0000_0011) << 3) | ((bytes[i + 4] & 0b1110_0000) >> 5)];
if (j == length) break;
chars[j++] = Base32Table[bytes[i + 4] & 0b0001_1111];
i += 5;
}
if (j < length)
chars[j++] = Base32Table[(bytes[i] & 0b1111_1000) >> 3];
if (j < length)
chars[j] = Base32Table[(bytes[i] & 0b0000_0111) << 2];
return new string(chars);
}
}
}
+18 -48
View File
@@ -29,7 +29,7 @@ namespace Umbraco.Core.IO
// shadow support
private readonly List<ShadowWrapper> _shadowWrappers = new List<ShadowWrapper>();
private readonly object _shadowLocker = new object();
private static string _shadowCurrentId; // static - unique!!
private static Guid _shadowCurrentId = Guid.Empty; // static - unique!!
#region Constructor
// DI wants a public ctor
@@ -45,13 +45,13 @@ namespace Umbraco.Core.IO
_shadowWrappers.Clear();
_filesystems.Clear();
Volatile.Write(ref _wkfsInitialized, false);
_shadowCurrentId = null;
_shadowCurrentId = Guid.Empty;
}
// for tests only, totally unsafe
internal static void ResetShadowId()
{
_shadowCurrentId = null;
_shadowCurrentId = Guid.Empty;
}
// set by the scope provider when taking control of filesystems
@@ -126,11 +126,11 @@ namespace Umbraco.Core.IO
var scriptsFileSystem = new PhysicalFileSystem(SystemDirectories.Scripts);
var mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews);
_macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "macro-partials", IsScoped);
_partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "partials", IsScoped);
_macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", IsScoped);
_partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", IsScoped);
_stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", IsScoped);
_scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", IsScoped);
_mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "views", IsScoped);
_mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", IsScoped);
// TODO: do we need a lock here?
_shadowWrappers.Add(_macroPartialFileSystem);
@@ -146,11 +146,6 @@ namespace Umbraco.Core.IO
#region Providers
private readonly Dictionary<Type, string> _paths = new Dictionary<Type, string>();
// internal for tests
internal IReadOnlyDictionary<Type, string> Paths => _paths;
/// <summary>
/// Gets a strongly-typed filesystem.
/// </summary>
@@ -167,33 +162,10 @@ namespace Umbraco.Core.IO
return (TFileSystem) _filesystems.GetOrAdd(typeof(TFileSystem), _ => new Lazy<IFileSystem>(() =>
{
var typeofTFileSystem = typeof(TFileSystem);
var name = typeof(TFileSystem).FullName;
if (name == null) throw new Exception("panic!");
// path must be unique and not collide with paths used in CreateWellKnownFileSystems
// for our well-known 'media' filesystem we can use the short 'media' path
// for others, put them under 'x/' and use ... something
string path;
if (typeofTFileSystem == typeof(MediaFileSystem))
{
path = "media";
}
else
{
lock (_paths)
{
if (!_paths.TryGetValue(typeofTFileSystem, out path))
{
path = Guid.NewGuid().ToString("N").Substring(0, 6);
while (_paths.ContainsValue(path)) // this can't loop forever, right?
path = Guid.NewGuid().ToString("N").Substring(0, 6);
_paths[typeofTFileSystem] = path;
}
}
path = "x/" + path;
}
var shadowWrapper = CreateShadowWrapper(supporting, path);
var shadowWrapper = CreateShadowWrapper(supporting, "typed/" + name);
return _container.CreateInstance<TFileSystem>(shadowWrapper);
})).Value;
}
@@ -207,37 +179,35 @@ namespace Umbraco.Core.IO
// global shadow for the entire application, so great care should be taken to ensure that the
// application is *not* doing anything else when using a shadow.
internal ICompletable Shadow()
internal ICompletable Shadow(Guid id)
{
if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems();
var id = ShadowWrapper.CreateShadowId();
return new ShadowFileSystems(this, id); // will invoke BeginShadow and EndShadow
}
internal void BeginShadow(string id)
internal void BeginShadow(Guid id)
{
lock (_shadowLocker)
{
// if we throw here, it means that something very wrong happened.
if (_shadowCurrentId != null)
if (_shadowCurrentId != Guid.Empty)
throw new InvalidOperationException("Already shadowing.");
_shadowCurrentId = id;
_logger.Debug<ShadowFileSystems>("Shadow '{ShadowId}'", _shadowCurrentId);
_logger.Debug<ShadowFileSystems>("Shadow '{ShadowId}'", id);
foreach (var wrapper in _shadowWrappers)
wrapper.Shadow(_shadowCurrentId);
wrapper.Shadow(id);
}
}
internal void EndShadow(string id, bool completed)
internal void EndShadow(Guid id, bool completed)
{
lock (_shadowLocker)
{
// if we throw here, it means that something very wrong happened.
if (_shadowCurrentId == null)
if (_shadowCurrentId == Guid.Empty)
throw new InvalidOperationException("Not shadowing.");
if (id != _shadowCurrentId)
throw new InvalidOperationException("Not the current shadow.");
@@ -258,7 +228,7 @@ namespace Umbraco.Core.IO
}
}
_shadowCurrentId = null;
_shadowCurrentId = Guid.Empty;
if (exceptions.Count > 0)
throw new AggregateException(completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions);
@@ -270,7 +240,7 @@ namespace Umbraco.Core.IO
lock (_shadowLocker)
{
var wrapper = new ShadowWrapper(filesystem, shadowPath, IsScoped);
if (_shadowCurrentId != null)
if (_shadowCurrentId != Guid.Empty)
wrapper.Shadow(_shadowCurrentId);
_shadowWrappers.Add(wrapper);
return wrapper;
@@ -7,7 +7,7 @@ namespace Umbraco.Core.IO.MediaPathSchemes
/// Implements a combined-guids media path scheme.
/// </summary>
/// <remarks>
/// <para>Path is "{combinedGuid}/{filename}" where combinedGuid is a combination of itemGuid and propertyGuid.</para>
/// <para>Path is "{combinedGuid}/{filename>}" where combinedGuid is a combination of itemGuid and propertyGuid.</para>
/// </remarks>
public class CombinedGuidsMediaPathScheme : IMediaPathScheme
{
@@ -16,9 +16,7 @@ namespace Umbraco.Core.IO.MediaPathSchemes
{
// assumes that cuid and puid keys can be trusted - and that a single property type
// for a single content cannot store two different files with the same name
var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid);
var directory = HexEncoder.Encode(combinedGuid.ToByteArray()/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/...
var directory = HexEncoder.Encode(GuidUtils.Combine(itemGuid, propertyGuid).ToByteArray()/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/...
return Path.Combine(directory, filename).Replace('\\', '/');
}
@@ -1,37 +0,0 @@
using System;
using System.IO;
namespace Umbraco.Core.IO.MediaPathSchemes
{
/// <summary>
/// Implements a unique directory media path scheme.
/// </summary>
/// <remarks>
/// <para>This scheme provides deterministic short paths, with potential collisions.</para>
/// </remarks>
public class UniqueMediaPathScheme : IMediaPathScheme
{
private const int DirectoryLength = 8;
/// <inheritdoc />
public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null)
{
var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid);
var directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength);
return Path.Combine(directory, filename).Replace('\\', '/');
}
/// <inheritdoc />
/// <remarks>
/// <para>Returning null so that <see cref="MediaFileSystem.DeleteMediaFiles"/> does *not*
/// delete any directory. This is because the above shortening of the Guid to 8 chars
/// means we're increasing the risk of collision, and we don't want to delete files
/// belonging to other media items.</para>
/// <para>And, at the moment, we cannot delete directory "only if it is empty" because of
/// race conditions. We'd need to implement locks in <see cref="MediaFileSystem"/> for
/// this.</para>
/// </remarks>
public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => null;
}
}
@@ -304,13 +304,7 @@ namespace Umbraco.Core.IO
// permissions to reach that path, but it may nevertheless be outside of
// our root path, due to relative segments, so better check
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar))
{
// this says that 4.7.2 supports long paths - but Windows does not
// https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception?view=netframework-4.7.2
if (path.Length > 260)
throw new PathTooLongException($"Path {path} is too long.");
return path;
}
// nothing prevents us to reach the file, security-wise, yet it is outside
// this filesystem's root - throw
+2 -2
View File
@@ -10,7 +10,7 @@ namespace Umbraco.Core.IO
private bool _completed;
// invoked by the filesystems when shadowing
public ShadowFileSystems(FileSystems fileSystems, string id)
public ShadowFileSystems(FileSystems fileSystems, Guid id)
{
_fileSystems = fileSystems;
Id = id;
@@ -19,7 +19,7 @@ namespace Umbraco.Core.IO
}
// for tests
public string Id { get; }
public Guid Id { get; }
// invoked by the scope when exiting, if completed
public void Complete()
+1 -27
View File
@@ -22,33 +22,7 @@ namespace Umbraco.Core.IO
_isScoped = isScoped;
}
public static string CreateShadowId()
{
const int retries = 50; // avoid infinite loop
const int idLength = 8; // 6 chars
// shorten a Guid to idLength chars, and see whether it collides
// with an existing directory or not - if it does, try again, and
// we should end up with a unique identifier eventually - but just
// detect infinite loops (just in case)
for (var i = 0; i < retries; i++)
{
var id = GuidUtils.ToBase32String(Guid.NewGuid(), idLength);
var virt = ShadowFsPath + "/" + id;
var shadowDir = IOHelper.MapPath(virt);
if (Directory.Exists(shadowDir))
continue;
Directory.CreateDirectory(shadowDir);
return id;
}
throw new Exception($"Could not get a shadow identifier (tried {retries} times)");
}
internal void Shadow(string id)
internal void Shadow(Guid id)
{
// note: no thread-safety here, because ShadowFs is thread-safe due to the check
// on ShadowFileSystemsScope.None - and if None is false then we should be running
+20 -2
View File
@@ -1,4 +1,6 @@
using System.IO;
using System;
using System.IO;
using System.Web;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.IO
@@ -10,7 +12,23 @@ namespace Umbraco.Core.IO
// TODO: Kill this off we don't have umbraco.config XML cache we now have NuCache
public static string GetContentCacheXml(IGlobalSettings globalSettings)
{
return Path.Combine(globalSettings.LocalTempPath, "umbraco.config");
switch (globalSettings.LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
return Path.Combine(cachePath, "umbraco.config");
case LocalTempStorage.Default:
return IOHelper.ReturnPath(Constants.AppSettings.ContentXML, "~/App_Data/umbraco.config");
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ namespace Umbraco.Core.Logging
}
// a lightweight disposable timer
private class LightDisposableTimer : DisposableObjectSlim
private class LightDisposableTimer : DisposableObject
{
private readonly Action<long> _callback;
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
@@ -22,7 +22,6 @@ namespace Umbraco.Core.Logging.Serilog
//Set this environment variable - so that it can be used in external config file
//add key="serilog:write-to:RollingFile.pathFormat" value="%BASEDIR%\logs\log.txt" />
Environment.SetEnvironmentVariable("BASEDIR", AppDomain.CurrentDomain.BaseDirectory, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("MACHINENAME", Environment.MachineName, EnvironmentVariableTarget.Process);
logConfig.MinimumLevel.Verbose() //Set to highest level of logging (as any sinks may want to restrict it to Errors only)
.Enrich.WithProcessId()
@@ -41,6 +41,8 @@ namespace Umbraco.Core.Logging.Serilog
var loggerConfig = new LoggerConfiguration();
loggerConfig
.MinimalConfiguration()
.OutputDefaultTextFile(LogEventLevel.Debug)
.OutputDefaultJsonFile()
.ReadFromConfigFile()
.ReadFromUserConfigFile();
@@ -1,5 +1,4 @@
using Umbraco.Core.Compose;
using Umbraco.Core.Composing;
using Umbraco.Core.Components;
namespace Umbraco.Core.Logging.Viewer
{
+1 -1
View File
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Logging
public void Stop(bool discardResults = false)
{ }
private class VoidDisposable : DisposableObjectSlim
private class VoidDisposable : DisposableObject
{
protected override void DisposeResources()
{ }
@@ -1,10 +1,10 @@
using System.Runtime.Serialization;
using Umbraco.Core.Models.Sections;
using Umbraco.Core.Models.Trees;
namespace Umbraco.Core.Manifest
{
[DataContract(Name = "section", Namespace = "")]
public class ManifestSection : ISection
public class ManifestBackOfficeSection : IBackOfficeSection
{
[DataMember(Name = "alias")]
public string Alias { get; set; }
+1 -1
View File
@@ -101,7 +101,7 @@ namespace Umbraco.Core.Manifest
var gridEditors = new List<GridEditor>();
var contentApps = new List<ManifestContentAppDefinition>();
var dashboards = new List<ManifestDashboard>();
var sections = new List<ManifestSection>();
var sections = new List<ManifestBackOfficeSection>();
foreach (var manifest in manifests)
{
+1 -1
View File
@@ -55,6 +55,6 @@ namespace Umbraco.Core.Manifest
/// Gets or sets the sections listed in the manifest.
/// </summary>
[JsonProperty("sections")]
public ManifestSection[] Sections { get; set; } = Array.Empty<ManifestSection>();
public ManifestBackOfficeSection[] Sections { get; set; } = Array.Empty<ManifestBackOfficeSection>();
}
}
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Logging;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
namespace Umbraco.Core.Migrations
@@ -34,11 +32,5 @@ namespace Umbraco.Core.Migrations
/// Gets or sets a value indicating whether an expression is being built.
/// </summary>
bool BuildingExpression { get; set; }
/// <summary>
/// Adds a post-migrations.
/// </summary>
void AddPostMigration<TMigration>()
where TMigration : IMigration;
}
}
@@ -0,0 +1,12 @@
using Semver;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Migrations
{
public interface IPostMigration : IDiscoverable
{
void Execute(string name, IScope scope, SemVersion originVersion, SemVersion targetVersion, ILogger logger);
}
}
@@ -27,6 +27,7 @@ namespace Umbraco.Core.Migrations.Install
private readonly IRuntimeState _runtime;
private readonly IMigrationBuilder _migrationBuilder;
private readonly IKeyValueService _keyValueService;
private readonly PostMigrationCollection _postMigrations;
private readonly ILogger _logger;
private DatabaseSchemaResult _databaseSchemaValidationResult;
@@ -34,7 +35,7 @@ namespace Umbraco.Core.Migrations.Install
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseBuilder"/> class.
/// </summary>
public DatabaseBuilder(IScopeProvider scopeProvider, IGlobalSettings globalSettings, IUmbracoDatabaseFactory databaseFactory, IRuntimeState runtime, ILogger logger, IMigrationBuilder migrationBuilder, IKeyValueService keyValueService)
public DatabaseBuilder(IScopeProvider scopeProvider, IGlobalSettings globalSettings, IUmbracoDatabaseFactory databaseFactory, IRuntimeState runtime, ILogger logger, IMigrationBuilder migrationBuilder, IKeyValueService keyValueService, PostMigrationCollection postMigrations)
{
_scopeProvider = scopeProvider;
_globalSettings = globalSettings;
@@ -43,6 +44,7 @@ namespace Umbraco.Core.Migrations.Install
_logger = logger;
_migrationBuilder = migrationBuilder;
_keyValueService = keyValueService;
_postMigrations = postMigrations;
}
#region Status
@@ -481,7 +483,7 @@ namespace Umbraco.Core.Migrations.Install
/// configured and it is possible to connect to the database.</para>
/// <para>Runs whichever migrations need to run.</para>
/// </remarks>
public Result UpgradeSchemaAndData(MigrationPlan plan)
public Result UpgradeSchemaAndData()
{
try
{
@@ -494,8 +496,8 @@ namespace Umbraco.Core.Migrations.Install
_logger.Info<DatabaseBuilder>("Database upgrade started");
// upgrade
var upgrader = new Upgrader(plan);
upgrader.Execute(_scopeProvider, _migrationBuilder, _keyValueService, _logger);
var upgrader = new UmbracoUpgrader();
upgrader.Execute(_scopeProvider, _migrationBuilder, _keyValueService, _logger, _postMigrations);
var message = "<p>Upgrade completed!</p>";
@@ -271,17 +271,17 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -88, EditorAlias = Constants.PropertyEditors.Aliases.TextBox, DbType = "Nvarchar" });
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -89, EditorAlias = Constants.PropertyEditors.Aliases.TextArea, DbType = "Ntext" });
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -90, EditorAlias = Constants.PropertyEditors.Aliases.UploadField, DbType = "Nvarchar" });
InsertDataTypeDto(Constants.DataTypes.LabelString, Constants.PropertyEditors.Aliases.Label, "Nvarchar", "{\"umbracoDataValueType\":\"STRING\"}");
InsertDataTypeDto(Constants.DataTypes.LabelInt, Constants.PropertyEditors.Aliases.Label, "Integer", "{\"umbracoDataValueType\":\"INT\"}");
InsertDataTypeDto(Constants.DataTypes.LabelBigint, Constants.PropertyEditors.Aliases.Label, "Nvarchar", "{\"umbracoDataValueType\":\"BIGINT\"}");
InsertDataTypeDto(Constants.DataTypes.LabelDateTime, Constants.PropertyEditors.Aliases.Label, "Date", "{\"umbracoDataValueType\":\"DATETIME\"}");
InsertDataTypeDto(Constants.DataTypes.LabelDecimal, Constants.PropertyEditors.Aliases.Label, "Decimal", "{\"umbracoDataValueType\":\"DECIMAL\"}");
InsertDataTypeDto(Constants.DataTypes.LabelTime, Constants.PropertyEditors.Aliases.Label, "Date", "{\"umbracoDataValueType\":\"TIME\"}");
InsertDataTypeDto(Constants.DataTypes.LabelString, Constants.PropertyEditors.Aliases.NoEdit, "Nvarchar", "{\"umbracoDataValueType\":\"STRING\"}");
InsertDataTypeDto(Constants.DataTypes.LabelInt, Constants.PropertyEditors.Aliases.NoEdit, "Integer", "{\"umbracoDataValueType\":\"INT\"}");
InsertDataTypeDto(Constants.DataTypes.LabelBigint, Constants.PropertyEditors.Aliases.NoEdit, "Nvarchar", "{\"umbracoDataValueType\":\"BIGINT\"}");
InsertDataTypeDto(Constants.DataTypes.LabelDateTime, Constants.PropertyEditors.Aliases.NoEdit, "Date", "{\"umbracoDataValueType\":\"DATETIME\"}");
InsertDataTypeDto(Constants.DataTypes.LabelDecimal, Constants.PropertyEditors.Aliases.NoEdit, "Decimal", "{\"umbracoDataValueType\":\"DECIMAL\"}");
InsertDataTypeDto(Constants.DataTypes.LabelTime, Constants.PropertyEditors.Aliases.NoEdit, "Date", "{\"umbracoDataValueType\":\"TIME\"}");
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -36, EditorAlias = Constants.PropertyEditors.Aliases.DateTime, DbType = "Date" });
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -37, EditorAlias = Constants.PropertyEditors.Aliases.ColorPicker, DbType = "Nvarchar" });
InsertDataTypeDto(Constants.DataTypes.DropDownSingle, Constants.PropertyEditors.Aliases.DropDownListFlexible, "Nvarchar", "{\"multiple\":false}");
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -40, EditorAlias = Constants.PropertyEditors.Aliases.RadioButtonList, DbType = "Nvarchar" });
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -41, EditorAlias = "Umbraco.DateTime", DbType = "Date", Configuration = "{\"format\":\"YYYY-MM-DD\"}" });
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -41, EditorAlias = Constants.PropertyEditors.Aliases.Date, DbType = "Date" });
InsertDataTypeDto(Constants.DataTypes.DropDownMultiple, Constants.PropertyEditors.Aliases.DropDownListFlexible, "Nvarchar", "{\"multiple\":true}");
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = -43, EditorAlias = Constants.PropertyEditors.Aliases.CheckBoxList, DbType = "Nvarchar" });
_database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1041, EditorAlias = Constants.PropertyEditors.Aliases.Tags, DbType = "Ntext",
@@ -321,7 +321,7 @@ namespace Umbraco.Core.Migrations.Install
{
// on install, initialize the umbraco migration plan with the final state
var upgrader = new Upgrader(new UmbracoPlan());
var upgrader = new UmbracoUpgrader();
var stateValueKey = upgrader.StateValueKey;
var finalState = upgrader.Plan.FinalState;
@@ -50,6 +50,8 @@ namespace Umbraco.Core.Migrations.Install
typeof (MemberTypeDto),
typeof (MemberDto),
typeof (Member2MemberGroupDto),
typeof (ContentXmlDto),
typeof (PreviewXmlDto),
typeof (PropertyTypeGroupDto),
typeof (PropertyTypeDto),
typeof (PropertyDataDto),
@@ -137,18 +139,13 @@ namespace Umbraco.Core.Migrations.Install
/// Validates the schema of the current database.
/// </summary>
internal DatabaseSchemaResult ValidateSchema()
{
return ValidateSchema(OrderedTables);
}
internal DatabaseSchemaResult ValidateSchema(IEnumerable<Type> orderedTables)
{
var result = new DatabaseSchemaResult(SqlSyntax);
result.IndexDefinitions.AddRange(SqlSyntax.GetDefinedIndexes(_database)
.Select(x => new DbIndexDefinition(x)));
result.TableDefinitions.AddRange(orderedTables
result.TableDefinitions.AddRange(OrderedTables
.Select(x => DefinitionFactory.GetTableDefinition(x, SqlSyntax)));
ValidateDbTables(result);
@@ -96,12 +96,6 @@ namespace Umbraco.Core.Migrations
return tables.Any(x => x.InvariantEquals(tableName));
}
protected bool IndexExists(string indexName)
{
var indexes = SqlSyntax.GetDefinedIndexes(Context.Database);
return indexes.Any(x => x.Item2.InvariantEquals(indexName));
}
protected bool ColumnExists(string tableName, string columnName)
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).Distinct().ToArray();
@@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
namespace Umbraco.Core.Migrations
{
/// <summary>
/// Implements <see cref="IMigrationContext"/>.
/// Represents a migration context.
/// </summary>
internal class MigrationContext : IMigrationContext
{
@@ -33,15 +32,5 @@ namespace Umbraco.Core.Migrations
/// <inheritdoc />
public bool BuildingExpression { get; set; }
// this is only internally exposed
public List<Type> PostMigrations { get; } = new List<Type>();
/// <inheritdoc />
public void AddPostMigration<TMigration>()
where TMigration : IMigration
{
PostMigrations.Add(typeof(TMigration));
}
}
}
@@ -14,7 +14,6 @@ namespace Umbraco.Core.Migrations
public class MigrationPlan
{
private readonly Dictionary<string, Transition> _transitions = new Dictionary<string, Transition>();
private readonly List<Type> _postMigrationTypes = new List<Type>();
private string _prevState;
private string _finalState;
@@ -167,25 +166,6 @@ namespace Umbraco.Core.Migrations
return this;
}
/// <summary>
/// Prepares post-migrations.
/// </summary>
/// <remarks>
/// <para>This can be overriden to filter, complement, and/or re-order post-migrations.</para>
/// </remarks>
protected virtual IEnumerable<Type> PreparePostMigrations(IEnumerable<Type> types)
=> types;
/// <summary>
/// Adds a post-migration to the plan.
/// </summary>
public virtual MigrationPlan AddPostMigration<TMigration>()
where TMigration : IMigration
{
_postMigrationTypes.Add(typeof(TMigration));
return this;
}
/// <summary>
/// Creates a random, unique state.
/// </summary>
@@ -290,7 +270,6 @@ namespace Umbraco.Core.Migrations
throw new Exception($"Unknown state \"{origState}\".");
var context = new MigrationContext(scope.Database, logger);
context.PostMigrations.AddRange(_postMigrationTypes);
while (transition != null)
{
@@ -306,20 +285,6 @@ namespace Umbraco.Core.Migrations
throw new Exception($"Unknown state \"{origState}\".");
}
// prepare and de-duplicate post-migrations, only keeping the 1st occurence
var temp = new HashSet<Type>();
var postMigrationTypes = PreparePostMigrations(context.PostMigrations)
.Where(x => !temp.Contains(x))
.Select(x => { temp.Add(x); return x; });
// run post-migrations
foreach (var postMigrationType in postMigrationTypes)
{
logger.Info<MigrationPlan>($"PostMigration: {postMigrationType.FullName}.");
var postMigration = migrationBuilder.Build(postMigrationType, context);
postMigration.Migrate();
}
logger.Info<MigrationPlan>("Done (pending scope completion).");
// safety check
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Migrations
{
public class PostMigrationCollection : BuilderCollectionBase<IPostMigration>
{
public PostMigrationCollection(IEnumerable<IPostMigration> items)
: base(items)
{ }
}
}
@@ -0,0 +1,11 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Migrations
{
public class PostMigrationCollectionBuilder : LazyCollectionBuilderBase<PostMigrationCollectionBuilder, PostMigrationCollection, IPostMigration>
{
protected override PostMigrationCollectionBuilder This => this;
protected override Lifetime CollectionLifetime => Lifetime.Transient;
}
}
@@ -1,18 +0,0 @@
namespace Umbraco.Core.Migrations.PostMigrations
{
/// <summary>
/// Rebuilds the published snapshot.
/// </summary>
/// <remarks>
/// <para>This interface exists because the entire published snapshot lives in Umbraco.Web
/// but we may want to trigger rebuilds from Umbraco.Core. These two assemblies should
/// be refactored, really.</para>
/// </remarks>
public interface IPublishedSnapshotRebuilder
{
/// <summary>
/// Rebuilds.
/// </summary>
void Rebuild();
}
}
@@ -1,12 +0,0 @@
namespace Umbraco.Core.Migrations.PostMigrations
{
/// <summary>
/// Implements <see cref="IPublishedSnapshotRebuilder"/> in Umbraco.Core (doing nothing).
/// </summary>
public class PublishedSnapshotRebuilder : IPublishedSnapshotRebuilder
{
/// <inheritdoc />
public void Rebuild()
{ }
}
}
@@ -1,24 +0,0 @@
namespace Umbraco.Core.Migrations.PostMigrations
{
/// <summary>
/// Rebuilds the published snapshot.
/// </summary>
public class RebuildPublishedSnapshot : IMigration
{
private readonly IPublishedSnapshotRebuilder _rebuilder;
/// <summary>
/// Initializes a new instance of the <see cref="RebuildPublishedSnapshot"/> class.
/// </summary>
public RebuildPublishedSnapshot(IPublishedSnapshotRebuilder rebuilder)
{
_rebuilder = rebuilder;
}
/// <inheritdoc />
public void Migrate()
{
_rebuilder.Rebuild();
}
}
}
@@ -56,7 +56,7 @@ namespace Umbraco.Core.Migrations.Upgrade
}
}
// define the plan
// define the plan
protected void DefinePlan()
{
// MODIFYING THE PLAN
@@ -127,16 +127,8 @@ namespace Umbraco.Core.Migrations.Upgrade
To<ConvertRelatedLinksToMultiUrlPicker>("{ED28B66A-E248-4D94-8CDB-9BDF574023F0}");
To<UpdatePickerIntegerValuesToUdi>("{38C809D5-6C34-426B-9BEA-EFD39162595C}");
To<RenameUmbracoDomainsTable>("{6017F044-8E70-4E10-B2A3-336949692ADD}");
To<AddUserLoginDtoDateIndex>("{98339BEF-E4B2-48A8-B9D1-D173DC842BBE}");
Merge()
.To<DropXmlTables>("{CDBEDEE4-9496-4903-9CF2-4104E00FF960}")
.With()
.To<RadioAndCheckboxAndDropdownPropertyEditorsMigration>("{940FD19A-00A8-4D5C-B8FF-939143585726}")
.As("{0576E786-5C30-4000-B969-302B61E90CA3}");
To<RenameLabelAndRichTextPropertyEditorAliases>("{E0CBE54D-A84F-4A8F-9B13-900945FD7ED9}");
To<MergeDateAndDateTimePropertyEditor>("{78BAF571-90D0-4D28-8175-EF96316DA789}");
//FINAL
@@ -0,0 +1,48 @@
using System;
using System.Configuration;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
namespace Umbraco.Core.Migrations.Upgrade
{
/// <summary>
/// Represents the Umbraco upgrader.
/// </summary>
public class UmbracoUpgrader : Upgrader
{
private PostMigrationCollection _postMigrations;
/// <summary>
/// Initializes a new instance of the <see ref="UmbracoUpgrader" /> class.
/// </summary>
public UmbracoUpgrader()
: base(new UmbracoPlan())
{ }
/// <summary>
/// Executes.
/// </summary>
public void Execute(IScopeProvider scopeProvider, IMigrationBuilder migrationBuilder, IKeyValueService keyValueService, ILogger logger, PostMigrationCollection postMigrations)
{
_postMigrations = postMigrations;
Execute(scopeProvider, migrationBuilder, keyValueService, logger);
}
/// <inheritdoc />
public override void AfterMigrations(IScope scope, ILogger logger)
{
// assume we have something in web.config that makes some sense = the origin version
if (!SemVersion.TryParse(ConfigurationManager.AppSettings[Constants.AppSettings.ConfigurationStatus], out var originVersion))
throw new InvalidOperationException($"Could not get current version from web.config {Constants.AppSettings.ConfigurationStatus} appSetting.");
// target version is the code version
var targetVersion = UmbracoVersion.SemanticVersion;
foreach (var postMigration in _postMigrations)
postMigration.Execute(Name, scope, originVersion, targetVersion, logger);
}
}
}
@@ -53,7 +53,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
var dataTypeDto = new DataTypeDto
{
NodeId = id,
EditorAlias = Constants.PropertyEditors.Aliases.Label,
EditorAlias = Constants.PropertyEditors.Aliases.NoEdit,
DbType = dbType
};
@@ -1,22 +0,0 @@
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
public class AddUserLoginDtoDateIndex : MigrationBase
{
public AddUserLoginDtoDateIndex(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
if (!IndexExists("IX_umbracoUserLogin_lastValidatedUtc"))
Create.Index("IX_umbracoUserLogin_lastValidatedUtc")
.OnTable(UserLoginDto.TableName)
.OnColumn("lastValidatedUtc")
.Ascending()
.WithOptions().NonClustered()
.Do();
}
}
}
@@ -99,6 +99,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
if (requiresCacheRebuild)
{
var dataTypeCacheRefresher = _cacheRefreshers[Guid.Parse("35B16C25-A17E-45D7-BC8F-EDAB1DCC28D2")];
// TODO: this should use DistributedCache not messengers, alas DistributedCache lives in Umbraco.Web
_serverMessenger.PerformRefreshAll(dataTypeCacheRefresher);
}
}
@@ -1,5 +1,6 @@
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
public class DropTaskTables : MigrationBase
{
public DropTaskTables(IMigrationContext context)
@@ -1,17 +0,0 @@
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
public class DropXmlTables : MigrationBase
{
public DropXmlTables(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
if (TableExists("cmsContentXml"))
Delete.Table("cmsContentXml").Do();
if (TableExists("cmsPreviewXml"))
Delete.Table("cmsPreviewXml").Do();
}
}
}
@@ -1,66 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
public class MergeDateAndDateTimePropertyEditor : MigrationBase
{
public MergeDateAndDateTimePropertyEditor(IMigrationContext context)
: base(context)
{
}
public override void Migrate()
{
var dataTypes = GetDataTypes("Umbraco.Date");
foreach (var dataType in dataTypes)
{
DateTimeConfiguration config;
try
{
config = (DateTimeConfiguration) new CustomDateTimeConfigurationEditor().FromDatabase(
dataType.Configuration);
}
catch (Exception ex)
{
Logger.Error<DropDownPropertyEditorsMigration>(
ex,
"Invalid property editor configuration detected: \"{Configuration}\", cannot convert editor, values will be cleared",
dataType.Configuration);
continue;
}
config.OffsetTime = false;
dataType.EditorAlias = Constants.PropertyEditors.Aliases.DateTime;
dataType.Configuration = ConfigurationEditor.ToDatabase(config);
Database.Update(dataType);
}
}
private List<DataTypeDto> GetDataTypes(string editorAlias)
{
//need to convert the old drop down data types to use the new one
var dataTypes = Database.Fetch<DataTypeDto>(Sql()
.Select<DataTypeDto>()
.From<DataTypeDto>()
.Where<DataTypeDto>(x => x.EditorAlias == editorAlias));
return dataTypes;
}
private class CustomDateTimeConfigurationEditor : ConfigurationEditor<DateTimeConfiguration>
{
}
}
}
@@ -1,183 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Umbraco.Core.Logging;
using Umbraco.Core.Migrations.PostMigrations;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
public class RadioAndCheckboxAndDropdownPropertyEditorsMigration : MigrationBase
{
public RadioAndCheckboxAndDropdownPropertyEditorsMigration(IMigrationContext context)
: base(context)
{
}
public override void Migrate()
{
var refreshCache = false;
refreshCache |= Migrate(Constants.PropertyEditors.Aliases.RadioButtonList, (dto, configuration) => UpdateRadioOrCheckboxPropertyDataDto(dto, configuration, true));
refreshCache |= Migrate(Constants.PropertyEditors.Aliases.CheckBoxList, (dto, configuration) => UpdateRadioOrCheckboxPropertyDataDto(dto, configuration, false));
refreshCache |= Migrate(Constants.PropertyEditors.Aliases.DropDownListFlexible, UpdateDropDownPropertyDataDto);
if (refreshCache)
{
Context.AddPostMigration<RebuildPublishedSnapshot>();
}
}
private bool Migrate(string editorAlias, Func<PropertyDataDto, ValueListConfiguration, bool> updateRadioPropertyDataFunc)
{
var refreshCache = false;
var dataTypes = GetDataTypes(editorAlias);
foreach (var dataType in dataTypes)
{
ValueListConfiguration config;
if (dataType.Configuration.IsNullOrWhiteSpace())
continue;
// parse configuration, and update everything accordingly
try
{
config = (ValueListConfiguration) new ValueListConfigurationEditor().FromDatabase(
dataType.Configuration);
}
catch (Exception ex)
{
Logger.Error<DropDownPropertyEditorsMigration>(
ex,
"Invalid property editor configuration detected: \"{Configuration}\", cannot convert editor, values will be cleared",
dataType.Configuration);
continue;
}
// get property data dtos
var propertyDataDtos = Database.Fetch<PropertyDataDto>(Sql()
.Select<PropertyDataDto>()
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyTypeDto, PropertyDataDto>((pt, pd) => pt.Id == pd.PropertyTypeId)
.InnerJoin<DataTypeDto>()
.On<DataTypeDto, PropertyTypeDto>((dt, pt) => dt.NodeId == pt.DataTypeId)
.Where<PropertyTypeDto>(x => x.DataTypeId == dataType.NodeId));
// update dtos
var updatedDtos = propertyDataDtos.Where(x => updateRadioPropertyDataFunc(x, config));
// persist changes
foreach (var propertyDataDto in updatedDtos) Database.Update(propertyDataDto);
UpdateDataType(dataType);
refreshCache = true;
}
return refreshCache;
}
private List<DataTypeDto> GetDataTypes(string editorAlias)
{
//need to convert the old drop down data types to use the new one
var dataTypes = Database.Fetch<DataTypeDto>(Sql()
.Select<DataTypeDto>()
.From<DataTypeDto>()
.Where<DataTypeDto>(x => x.EditorAlias == editorAlias));
return dataTypes;
}
private void UpdateDataType(DataTypeDto dataType)
{
dataType.DbType = ValueStorageType.Nvarchar.ToString();
Database.Update(dataType);
}
private bool UpdateRadioOrCheckboxPropertyDataDto(PropertyDataDto propData, ValueListConfiguration config, bool singleValue)
{
//Get the INT ids stored for this property/drop down
int[] ids = null;
if (!propData.VarcharValue.IsNullOrWhiteSpace())
{
ids = ConvertStringValues(propData.VarcharValue);
}
else if (!propData.TextValue.IsNullOrWhiteSpace())
{
ids = ConvertStringValues(propData.TextValue);
}
else if (propData.IntegerValue.HasValue)
{
ids = new[] {propData.IntegerValue.Value};
}
//if there are INT ids, convert them to values based on the configuration
if (ids == null || ids.Length <= 0) return false;
//map the ids to values
var values = new List<string>();
var canConvert = true;
foreach (var id in ids)
{
var val = config.Items.FirstOrDefault(x => x.Id == id);
if (val != null)
values.Add(val.Value);
else
{
Logger.Warn<DropDownPropertyEditorsMigration>(
"Could not find associated data type configuration for stored Id {DataTypeId}", id);
canConvert = false;
}
}
if (!canConvert) return false;
//The radio button only supports selecting a single value, so if there are multiple for some insane reason we can only use the first
propData.VarcharValue = singleValue ? values[0] : JsonConvert.SerializeObject(values);
propData.TextValue = null;
propData.IntegerValue = null;
return true;
}
private bool UpdateDropDownPropertyDataDto(PropertyDataDto propData, ValueListConfiguration config)
{
//Get the INT ids stored for this property/drop down
var values = propData.VarcharValue.Split(new []{","}, StringSplitOptions.RemoveEmptyEntries);
//if there are INT ids, convert them to values based on the configuration
if (values == null || values.Length <= 0) return false;
//The radio button only supports selecting a single value, so if there are multiple for some insane reason we can only use the first
propData.VarcharValue = JsonConvert.SerializeObject(values);
propData.TextValue = null;
propData.IntegerValue = null;
return true;
}
private int[] ConvertStringValues(string val)
{
var splitVals = val.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
var intVals = splitVals
.Select(x => int.TryParse(x, out var i) ? i : int.MinValue)
.Where(x => x != int.MinValue)
.ToArray();
//only return if the number of values are the same (i.e. All INTs)
if (splitVals.Length == intVals.Length)
return intVals;
return null;
}
private class ValueListConfigurationEditor : ConfigurationEditor<ValueListConfiguration>
{
}
}
}
@@ -1,41 +0,0 @@
using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
public class RenameLabelAndRichTextPropertyEditorAliases : MigrationBase
{
public RenameLabelAndRichTextPropertyEditorAliases(IMigrationContext context)
: base(context)
{
}
public override void Migrate()
{
MigratePropertyEditorAlias("Umbraco.TinyMCEv3", Constants.PropertyEditors.Aliases.TinyMce);
MigratePropertyEditorAlias("Umbraco.NoEdit", Constants.PropertyEditors.Aliases.Label);
}
private void MigratePropertyEditorAlias(string oldAlias, string newAlias)
{
var dataTypes = GetDataTypes(oldAlias);
foreach (var dataType in dataTypes)
{
dataType.EditorAlias = newAlias;
Database.Update(dataType);
}
}
private List<DataTypeDto> GetDataTypes(string editorAlias)
{
var dataTypes = Database.Fetch<DataTypeDto>(Sql()
.Select<DataTypeDto>()
.From<DataTypeDto>()
.Where<DataTypeDto>(x => x.EditorAlias == editorAlias));
return dataTypes;
}
}
}
@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Collections;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
@@ -105,6 +101,35 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Validates the content item's properties pass variant rules
/// </summary>
/// <para>If the content type is variant, then culture can be either '*' or an actual culture, but neither 'null' nor
/// 'empty'. If the content type is invariant, then culture can be either '*' or null or empty.</para>
public static Property[] ValidateProperties(this IContentBase content, string culture = "*")
{
// select invalid properties
return content.Properties.Where(x =>
{
// if culture is null, we validate invariant properties only
// if culture is '*' we validate both variant and invariant properties, automatically
// if culture is specific eg 'en-US' we both too, but explicitly
var varies = x.PropertyType.VariesByCulture();
if (culture == null)
return !(varies || x.IsValid(null)); // validate invariant property, invariant culture
if (culture == "*")
return !x.IsValid(culture); // validate property, all cultures
return varies
? !x.IsValid(culture) // validate variant property, explicit culture
: !x.IsValid(null); // validate invariant property, explicit culture
})
.ToArray();
}
public static void SetPublishInfo(this IContent content, string culture, string name, DateTime date)
{
if (string.IsNullOrWhiteSpace(name))
@@ -166,13 +191,6 @@ namespace Umbraco.Core.Models
content.CultureInfos.AddOrUpdate(culture, name, date);
}
/// <summary>
/// Sets the publishing values for names and properties.
/// </summary>
/// <param name="content"></param>
/// <param name="culture"></param>
/// <returns>A value indicating whether it was possible to publish the names and values for the specified
/// culture(s). The method may fail if required names are not set, but it does NOT validate property data</returns>
public static bool PublishCulture(this IContent content, string culture = "*")
{
culture = culture.NullOrWhiteSpaceAsNull();
@@ -183,6 +201,10 @@ namespace Umbraco.Core.Models
if (!content.ContentType.SupportsPropertyVariation(culture, "*", true))
throw new NotSupportedException($"Culture \"{culture}\" is not supported by content type \"{content.ContentType.Alias}\" with variation \"{content.ContentType.Variations}\".");
// the values we want to publish should be valid
if (content.ValidateProperties(culture).Any())
return false;
var alsoInvariant = false;
if (culture == "*") // all cultures
{
+2 -2
View File
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class ContentType : ContentTypeCompositionBase, IContentType
{
public const bool SupportsPublishingConst = true;
public const bool IsPublishingConst = true;
private int _defaultTemplate;
private IEnumerable<ITemplate> _allowedTemplates;
@@ -45,7 +45,7 @@ namespace Umbraco.Core.Models
public override ISimpleContentType ToSimple() => new SimpleContentType(this);
/// <inheritdoc />
public override bool SupportsPublishing => SupportsPublishingConst;
public override bool IsPublishing => IsPublishingConst;
//Custom comparer for enumerable
private static readonly DelegateEqualityComparer<IEnumerable<ITemplate>> TemplateComparer = new DelegateEqualityComparer<IEnumerable<ITemplate>>(
+5 -5
View File
@@ -40,7 +40,7 @@ namespace Umbraco.Core.Models
// actually OK as IsPublishing is constant
// ReSharper disable once VirtualMemberCallInConstructor
_noGroupPropertyTypes = new PropertyTypeCollection(SupportsPublishing);
_noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing);
_noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged;
_variations = ContentVariation.Nothing;
@@ -61,7 +61,7 @@ namespace Umbraco.Core.Models
// actually OK as IsPublishing is constant
// ReSharper disable once VirtualMemberCallInConstructor
_noGroupPropertyTypes = new PropertyTypeCollection(SupportsPublishing);
_noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing);
_noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged;
_variations = ContentVariation.Nothing;
@@ -70,7 +70,7 @@ namespace Umbraco.Core.Models
public abstract ISimpleContentType ToSimple();
/// <summary>
/// Gets a value indicating whether the content type supports publishing.
/// Gets a value indicating whether the content type is publishing.
/// </summary>
/// <remarks>
/// <para>A publishing content type supports draft and published values for properties.
@@ -80,7 +80,7 @@ namespace Umbraco.Core.Models
/// the draft or published value of a property returns the same thing, and publishing
/// a value property has no effect.</para>
/// </remarks>
public abstract bool SupportsPublishing { get; }
public abstract bool IsPublishing { get; }
//Custom comparer for enumerable
private static readonly DelegateEqualityComparer<IEnumerable<ContentTypeSort>> ContentTypeSortComparer =
@@ -257,7 +257,7 @@ namespace Umbraco.Core.Models
{
if (_noGroupPropertyTypes != null)
_noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged;
_noGroupPropertyTypes = new PropertyTypeCollection(SupportsPublishing, value);
_noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing, value);
_noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged;
PropertyTypesChanged(_noGroupPropertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
@@ -226,7 +226,7 @@ namespace Umbraco.Core.Models
return null;
// create the new group
var group = new PropertyGroup(SupportsPublishing) { Name = name, SortOrder = 0 };
var group = new PropertyGroup(IsPublishing) { 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
+2 -2
View File
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class MediaType : ContentTypeCompositionBase, IMediaType
{
public const bool SupportsPublishingConst = false;
public const bool IsPublishingConst = false;
/// <summary>
/// Constuctor for creating a MediaType with the parent's id.
@@ -45,7 +45,7 @@ namespace Umbraco.Core.Models
public override ISimpleContentType ToSimple() => new SimpleContentType(this);
/// <inheritdoc />
public override bool SupportsPublishing => SupportsPublishingConst;
public override bool IsPublishing => IsPublishingConst;
/// <inheritdoc />
IMediaType IMediaType.DeepCloneWithResetIdentities(string newAlias) => (IMediaType)DeepCloneWithResetIdentities(newAlias);
+2 -2
View File
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class MemberType : ContentTypeCompositionBase, IMemberType
{
public const bool SupportsPublishingConst = false;
public const bool IsPublishingConst = false;
//Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId
private string _alias;
@@ -35,7 +35,7 @@ namespace Umbraco.Core.Models
public override ISimpleContentType ToSimple() => new SimpleContentType(this);
/// <inheritdoc />
public override bool SupportsPublishing => SupportsPublishingConst;
public override bool IsPublishing => IsPublishingConst;
public override ContentVariation Variations
{
@@ -22,7 +22,6 @@ namespace Umbraco.Core.Models.Packaging
public RequirementsType UmbracoVersionRequirementsType { get; set; }
public string Author { get; set; }
public string AuthorUrl { get; set; }
public IList<string> Contributors { get; set; }
public string Readme { get; set; }
public string PackageView { get; set; }
public string IconUrl { get; set; }

Some files were not shown because too many files have changed in this diff Show More