Compare commits
239 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 173ac65eb5 | |||
| 7c28e1aeba | |||
| 5989d19f7f | |||
| da1c33a034 | |||
| 2111a5e31e | |||
| 5478de911b | |||
| 7765acb130 | |||
| 5640daff32 | |||
| a1cae3f286 | |||
| d22dbb4654 | |||
| 4d5a8298b0 | |||
| 9e2733ce69 | |||
| fbfdd8d398 | |||
| 2552dffdad | |||
| 52540165f1 | |||
| 2b408df24c | |||
| d559411187 | |||
| 3a4a6579d3 | |||
| adb059e0da | |||
| 4337976887 | |||
| efe5de87a6 | |||
| 389642946e | |||
| 53d30aa3cb | |||
| e233cffb9d | |||
| 9e58ebed24 | |||
| f51c4e332e | |||
| 03a0a0a0e9 | |||
| dcb381770f | |||
| 65db0e69a9 | |||
| 23c6a91e9c | |||
| a03c6dcf13 | |||
| 3029d97f33 | |||
| f41d967864 | |||
| 4a481b05d4 | |||
| 66c6b5b524 | |||
| e3b0f71549 | |||
| 973b466798 | |||
| fa0eef4333 | |||
| a343a76a26 | |||
| d0a9e3236e | |||
| b899805ec9 | |||
| 766497e355 | |||
| 67a2a9cfe4 | |||
| 766ba0c8f5 | |||
| ca71c59880 | |||
| 6c87cf729a | |||
| 5c63afa31a | |||
| fa88768c91 | |||
| bfabfd9fa5 | |||
| aa1c411c8c | |||
| c52c452b36 | |||
| 86811fe507 | |||
| 363de53b82 | |||
| 958fb66f2c | |||
| 30e12c1367 | |||
| 89ff7d85e2 | |||
| 61fbf2cfc6 | |||
| ce847e97a8 | |||
| 1e61563f79 | |||
| b4f9985695 | |||
| 82f19ede8c | |||
| 0fbc32a87f | |||
| dc2de4b5e7 | |||
| 5fce741940 | |||
| 14e4a8061c | |||
| 1b9f071581 | |||
| 79141ef86d | |||
| 08609a887c | |||
| b11932240c | |||
| d47b4517c1 | |||
| 226a13c6e1 | |||
| d78a03a610 | |||
| b7a1123319 | |||
| 7e9cad34db | |||
| c4b44ea0e3 | |||
| 3435a75e9e | |||
| bdb6c5768a | |||
| 87a3c53e22 | |||
| 898d25f3cb | |||
| 9bc3fdd555 | |||
| cbda86fe92 | |||
| b48f0f52e0 | |||
| e2eeafcbfc | |||
| ad697db42b | |||
| 9bb31e9456 | |||
| a195d33332 | |||
| 82a87cbd92 | |||
| 5b26436862 | |||
| eed61f9053 | |||
| 55c68485e9 | |||
| b9ba350a2f | |||
| 696306e7c9 | |||
| 9bf86aff09 | |||
| f50de5b049 | |||
| d734ba6136 | |||
| c49becea53 | |||
| 614aea203d | |||
| ed88bbf75f | |||
| e41d3c7e84 | |||
| a099af39c2 | |||
| 118f9b02e2 | |||
| 38778cc7c7 | |||
| ffff9efaa8 | |||
| 7f4abb4893 | |||
| 4d39ce1202 | |||
| 7164f23a98 | |||
| 8b2d1b8eab | |||
| 80394f08fb | |||
| ca1fe5ff2a | |||
| a1037ce43c | |||
| 2cd2f3741d | |||
| e634dba4ea | |||
| d1eb8fd9eb | |||
| 2e91c89c02 | |||
| 8be54ba0cc | |||
| 3da7682415 | |||
| 775892ff5a | |||
| f30b4c8184 | |||
| 319ba5c791 | |||
| 398bc6d767 | |||
| 5d3ef8d97f | |||
| 08a300fbe5 | |||
| 845770c4bb | |||
| 771dc10c85 | |||
| 2ac76202e5 | |||
| a5ecbe96e2 | |||
| 7240b22e48 | |||
| 1f00589cb4 | |||
| 60b14487a9 | |||
| 33bc85a843 | |||
| e6642674d4 | |||
| 8ad5fb3c44 | |||
| 1752d14c57 | |||
| ec4ccba1a7 | |||
| 0244492a89 | |||
| 546cf2c84d | |||
| d354dde774 | |||
| 22c9e9acd9 | |||
| 5dd14723e9 | |||
| 7134e80c44 | |||
| f7196ee8f9 | |||
| fdecd697b7 | |||
| 6c03dac869 | |||
| 6cdb564a9f | |||
| 0da8a8db36 | |||
| a4a9620889 | |||
| 2307424fb6 | |||
| ca124fb662 | |||
| 6787252f9b | |||
| ef3094f8d9 | |||
| a2a20133aa | |||
| bb750d186a | |||
| f2a7dde5ce | |||
| 73e2559a13 | |||
| 80f8a9d01c | |||
| 6f9883d373 | |||
| 41c920a4ec | |||
| 1f79b71738 | |||
| 64fb588556 | |||
| 90cfd004cb | |||
| ad927ec6c1 | |||
| 8a19d28859 | |||
| 7e24af9daf | |||
| 7518b514a7 | |||
| 96c38f50e4 | |||
| e723f1169f | |||
| 914005b2b6 | |||
| 972b903f52 | |||
| 61d072c8d6 | |||
| 8740848aac | |||
| 6766aa9732 | |||
| a57334d1f7 | |||
| 3dea16fad1 | |||
| 1bf4121796 | |||
| 93bebdd50b | |||
| 6a9532f10a | |||
| d88494e90e | |||
| e48f08b657 | |||
| a79e349f3d | |||
| 1c70570078 | |||
| 197dacfae7 | |||
| d8aad718d5 | |||
| 1d7bfc2781 | |||
| 8063717e4c | |||
| 3900b7ebc8 | |||
| 0296c646cb | |||
| 275dbc83dd | |||
| 1cd2dc8a42 | |||
| 60867af1a7 | |||
| 1e129bcc4d | |||
| e5c758bbc1 | |||
| 25780663a6 | |||
| aa327696fe | |||
| 1272a7c74b | |||
| dcbe6726b0 | |||
| ba68da5736 | |||
| fdabd945dc | |||
| 868edee5e6 | |||
| b91d0bb117 | |||
| cc91977e94 | |||
| 90b94f6ffb | |||
| 3c4cf56309 | |||
| b7f6195791 | |||
| 42eb82e6ca | |||
| 93def731ef | |||
| 60805f91d6 | |||
| 048480d5be | |||
| 173d388c12 | |||
| b1285fa7e3 | |||
| e34c857f6c | |||
| 7ff0e6cd4b | |||
| 0683094f84 | |||
| 983b7a85e1 | |||
| d015780b9f | |||
| aeece499bb | |||
| 8465b4e556 | |||
| e280785f48 | |||
| 5e0a59e4b3 | |||
| 30835797ff | |||
| 4077a01a6b | |||
| aed70ae40c | |||
| 6d44628352 | |||
| 7a398d34b1 | |||
| 69c5aeb1b3 | |||
| da545b890b | |||
| 6609bc09f2 | |||
| f06a6f42da | |||
| c9065d96a9 | |||
| 9d4e1362c4 | |||
| d3b69e04e1 | |||
| 0d54e8df89 | |||
| 8f949d0a2f | |||
| d67480e98b | |||
| 5d76ead469 | |||
| 2796100067 | |||
| a132f609d7 | |||
| 7f7456701d | |||
| ffce1a3e6b | |||
| b12f60e443 |
@@ -79,3 +79,8 @@ src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
|
||||
src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
|
||||
build/_BuildOutput/
|
||||
tools/NDepend/
|
||||
src/*.vspx
|
||||
src/*.psess
|
||||
NDependOut/*
|
||||
QueryResult.htm
|
||||
*.ndproj
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# The MIT License (MIT) #
|
||||
|
||||
Copyright (c) 2013 Umbraco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -8,7 +8,7 @@ Umbraco CMS
|
||||
|
||||
**More than 177,000 sites trust Umbraco**
|
||||
|
||||
For the first time on the Microsoft platform a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports your ASP.NET User and Custom Controls out of the box. It's a developers dream and your users will love it too.
|
||||
For the first time on the Microsoft platform a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, out of the box. It's a developers dream and your users will love it too.
|
||||
|
||||
Used by more than 177,000 active websites including [http://daviscup.com](http://daviscup.com), [http://heinz.com](http://heinz.com), [http://peugeot.com](http://peugeot.com), [http://www.hersheys.com/](http://www.hersheys.com/) and **The Official ASP.NET and IIS.NET website from Microsoft** ([http://asp.net](http://asp.net) / [http://iis.net](http://iis.net)) you can be sure that the technology is proven, stable and scales.
|
||||
|
||||
@@ -16,7 +16,7 @@ To view more examples please visit [http://umbraco.com/why-umbraco/#caseStudies]
|
||||
|
||||
## Downloading ##
|
||||
|
||||
The downloadable Umbraco releases live at [http://our.umbraco.org/contribute/releases](http://our.umbraco.org/contribute/releases).
|
||||
The downloadable Umbraco releases live at [http://our.umbraco.org/download](http://our.umbraco.org/download).
|
||||
|
||||
## Forums ##
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
@ECHO OFF
|
||||
SET release=6.1.2
|
||||
SET release=6.1.5
|
||||
SET comment=
|
||||
SET version=%release%
|
||||
|
||||
|
||||
+12
-1
@@ -228,7 +228,7 @@
|
||||
</Target>
|
||||
|
||||
<!-- Copy the xml documentation to the bin folder -->
|
||||
<Target Name="CopyXmlDocumentation" DependsOnTargets="CompileProjects">
|
||||
<Target Name="CopyXmlDocumentation" DependsOnTargets="CleanupPresentation">
|
||||
<ItemGroup>
|
||||
<XmlDocumentationFiles Include="$(SolutionBinFolder)*.xml" />
|
||||
</ItemGroup>
|
||||
@@ -240,6 +240,17 @@
|
||||
<Message Text="CopyXmlDocumentation" />
|
||||
</Target>
|
||||
|
||||
<!-- Unlike 2010, the VS2012 build targets file doesn't clean up the umbraco.presentation dir, do it manually -->
|
||||
<!-- Also, it places umbraco.XmlSerializers.dll in the root, even though it's also in the bin folder. Kill! -->
|
||||
<Target Name="CleanupPresentation" DependsOnTargets="CompileProjects">
|
||||
<ItemGroup>
|
||||
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
|
||||
<XmlSerializersDll Include="$(WebAppFolder)umbraco.XmlSerializers.dll" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(PresentationFolderToDelete)" />
|
||||
<Delete Files="@(XmlSerializersDll)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CompileProjects" DependsOnTargets="CompileExternalExamineProjects">
|
||||
|
||||
<Message Text="Compiling web project to build\$(BuildFolder)" Importance="high" />
|
||||
|
||||
@@ -19,22 +19,22 @@
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="4.0" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" />
|
||||
<dependency id="MiniProfiler" version="2.1.0" />
|
||||
<dependency id="HtmlAgilityPack" version="1.4.5" />
|
||||
<dependency id="HtmlAgilityPack" version="1.4.6" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1]" />
|
||||
<dependency id="SharpZipLib" version="0.86.0" />
|
||||
<dependency id="MySql.Data" version="[6.6,6.7)" />
|
||||
<dependency id="MySql.Data" version="[6.6.5]" />
|
||||
<dependency id="xmlrpcnet" version="2.5.0" />
|
||||
<dependency id="ClientDependency" version="[1.7.0,1.7.1)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0,1.7.1)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\ClientDependency.Core.dll" target="lib\ClientDependency.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\ClientDependency.Core.Mvc.dll" target="lib\ClientDependency.Core.Mvc.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\cms.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\CookComputing.XmlRpcV2.dll" target="lib\CookComputing.XmlRpcV2.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Examine.dll" target="lib\Examine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\interfaces.xml" />
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Installs Umbraco Cms in your Visual Studio ASP.NET Webforms project</description>
|
||||
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET Webforms project</summary>
|
||||
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
|
||||
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
@@ -44,5 +44,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\Web.config" target="UmbracoFiles\Web.config" />
|
||||
<file src="tools\install.ps1" target="tools\install.ps1" />
|
||||
<file src="tools\Readme.txt" target="tools\Readme.txt" />
|
||||
<file src="build\**" target="build" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
|
||||
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="AddUmbracoFilesToOutput">
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include=".\umbraco\**\*;" />
|
||||
<UmbracoClientFiles Include=".\umbraco_client\**\*;" />
|
||||
<AppBrowsersFiles Include=".\App_Browsers\**\*" />
|
||||
<FilesForPackagingFromProject Include="%(UmbracoFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(UmbracoClientFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco_client\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(AppBrowsersFiles.Identity)">
|
||||
<DestinationRelativePath>App_Browsers\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
|
||||
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="AddUmbracoFilesToOutput">
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include=".\umbraco\**\*;" />
|
||||
<UmbracoClientFiles Include=".\umbraco_client\**\*;" />
|
||||
<AppBrowsersFiles Include=".\App_Browsers\**\*" />
|
||||
<FilesForPackagingFromProject Include="%(UmbracoFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(UmbracoClientFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco_client\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(AppBrowsersFiles.Identity)">
|
||||
<DestinationRelativePath>App_Browsers\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
|
||||
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="AddUmbracoFilesToOutput">
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include=".\umbraco\**\*;" />
|
||||
<UmbracoClientFiles Include=".\umbraco_client\**\*;" />
|
||||
<AppBrowsersFiles Include=".\App_Browsers\**\*" />
|
||||
<FilesForPackagingFromProject Include="%(UmbracoFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(UmbracoClientFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco_client\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(AppBrowsersFiles.Identity)">
|
||||
<DestinationRelativePath>App_Browsers\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -7,10 +7,11 @@ The config files found in the config folder will usually not be changed for patc
|
||||
but the web.config will have to have its previous "umbracoConfigurationStatus"-appsetting and "umbracoDbDSN" connection string
|
||||
copied over (as a minimum).
|
||||
|
||||
It's not possible to create a deploy package from Visual Studio, which contains everything out of the box. This is
|
||||
due to a number of files and folders that are not added to the Visual Studio project. These folders include:
|
||||
install, umbraco and umbraco_client.
|
||||
A custom deploy script will be added in a later release.
|
||||
This nuget package includes build targets that extends the creation of a deploy package, which is generated by
|
||||
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
|
||||
Publish this won't affect you.
|
||||
These folders will now be automatically included when creating a deploy package or publishing to the file system:
|
||||
umbraco and umbraco_client.
|
||||
|
||||
Please read the release notes on our.umbraco.org:
|
||||
http://our.umbraco.org/contribute/releases
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Umbraco.Core.Cache
|
||||
public const string UserContextCacheKey = "UmbracoUserContext";
|
||||
public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
|
||||
public const string UserCacheKey = "UmbracoUser";
|
||||
public const string UserPermissionsCacheKey = "UmbracoUserPermissions";
|
||||
|
||||
public const string ContentTypeCacheKey = "UmbracoContentType";
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Cache
|
||||
/// An abstract class for implementing a runtime cache provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC...
|
||||
/// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC...
|
||||
/// </remarks>
|
||||
internal abstract class RuntimeCacheProviderBase : CacheProviderBase
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using System.Web.Security;
|
||||
using System.Xml;
|
||||
using System.Configuration;
|
||||
|
||||
@@ -299,6 +300,32 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
internal static string AuthCookieName
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = GetKey("/settings/security/authCookieName");
|
||||
if (string.IsNullOrEmpty(value) == false)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return "UMB_UCONTEXT";
|
||||
}
|
||||
}
|
||||
|
||||
internal static string AuthCookieDomain
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = GetKey("/settings/security/authCookieDomain");
|
||||
if (string.IsNullOrEmpty(value) == false)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return FormsAuthentication.CookieDomain;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the experimental canvas (live) editing on the frontend of the website
|
||||
/// </summary>
|
||||
@@ -1305,6 +1332,25 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default document type property used when adding new properties through the back-office
|
||||
/// </summary>
|
||||
/// <value>Configured text for the default document type property</value>
|
||||
/// <remarks>If undefined, 'Textstring' is the default</remarks>
|
||||
public static string DefaultDocumentTypeProperty
|
||||
{
|
||||
get
|
||||
{
|
||||
var defaultDocumentTypeProperty = GetKey("/settings/content/defaultDocumentTypeProperty");
|
||||
if (string.IsNullOrEmpty(defaultDocumentTypeProperty))
|
||||
{
|
||||
defaultDocumentTypeProperty = "Textstring";
|
||||
}
|
||||
|
||||
return defaultDocumentTypeProperty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration regarding webservices
|
||||
/// </summary>
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("6.1.2");
|
||||
private static readonly Version Version = new Version("6.1.5");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Umbraco.Core
|
||||
public void ConfigureEmbeddedDatabaseConnection()
|
||||
{
|
||||
const string providerName = "System.Data.SqlServerCe.4.0";
|
||||
const string connectionString = @"Data Source=|DataDirectory|\Umbraco.sdf";
|
||||
const string connectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;File Access Retry Timeout=10";
|
||||
|
||||
SaveConnectionString(connectionString, providerName);
|
||||
|
||||
@@ -169,6 +169,20 @@ namespace Umbraco.Core
|
||||
Initialize(providerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures a ConnectionString for the Umbraco database that uses Microsoft SQL Server integrated security.
|
||||
/// </summary>
|
||||
/// <param name="server">Name or address of the database server</param>
|
||||
/// <param name="databaseName">Name of the database</param>
|
||||
public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName)
|
||||
{
|
||||
const string providerName = "System.Data.SqlClient";
|
||||
string connectionString = String.Format("Server={0};Database={1};Integrated Security=true", server, databaseName);
|
||||
|
||||
SaveConnectionString(connectionString, providerName);
|
||||
Initialize(providerName);
|
||||
}
|
||||
|
||||
internal string BuildAzureConnectionString(string server, string databaseName, string user, string password)
|
||||
{
|
||||
if (server.Contains(".") && ServerStartsWithTcp(server) == false)
|
||||
@@ -282,16 +296,15 @@ namespace Umbraco.Core
|
||||
if (databaseSettings != null && string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) == false && string.IsNullOrWhiteSpace(databaseSettings.ProviderName) == false)
|
||||
{
|
||||
var providerName = "System.Data.SqlClient";
|
||||
string connString = null;
|
||||
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName))
|
||||
{
|
||||
providerName = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName;
|
||||
|
||||
_connectionString =
|
||||
ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString;
|
||||
|
||||
connString = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString;
|
||||
}
|
||||
Initialize(providerName, connString);
|
||||
|
||||
Initialize(providerName);
|
||||
DetermineSqlServerVersion();
|
||||
}
|
||||
else if (ConfigurationManager.AppSettings.ContainsKey(GlobalSettings.UmbracoConnectionName) && string.IsNullOrEmpty(ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName]) == false)
|
||||
{
|
||||
@@ -328,6 +341,8 @@ namespace Umbraco.Core
|
||||
|
||||
//Remove the legacy connection string, so we don't end up in a loop if something goes wrong.
|
||||
GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName);
|
||||
|
||||
DetermineSqlServerVersion();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -355,6 +370,55 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
internal void Initialize(string providerName, string connectionString)
|
||||
{
|
||||
_connectionString = connectionString;
|
||||
Initialize(providerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the lazy resolution of determining the SQL server version if that is the db type we're using
|
||||
/// </summary>
|
||||
private void DetermineSqlServerVersion()
|
||||
{
|
||||
|
||||
var sqlServerSyntax = SqlSyntaxContext.SqlSyntaxProvider as SqlServerSyntaxProvider;
|
||||
if (sqlServerSyntax != null)
|
||||
{
|
||||
//this will not execute now, it is lazy so will only execute when we need to actually know
|
||||
// the sql server version.
|
||||
sqlServerSyntax.VersionName = new Lazy<SqlServerVersionName>(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var database = this._factory.CreateDatabase();
|
||||
|
||||
var version = database.ExecuteScalar<string>("SELECT SERVERPROPERTY('productversion')");
|
||||
var firstPart = version.Split('.')[0];
|
||||
switch (firstPart)
|
||||
{
|
||||
case "11":
|
||||
return SqlServerVersionName.V2012;
|
||||
case "10":
|
||||
return SqlServerVersionName.V2008;
|
||||
case "9":
|
||||
return SqlServerVersionName.V2005;
|
||||
case "8":
|
||||
return SqlServerVersionName.V2000;
|
||||
case "7":
|
||||
return SqlServerVersionName.V7;
|
||||
default:
|
||||
return SqlServerVersionName.Other;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return SqlServerVersionName.Invalid;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal DatabaseSchemaResult ValidateDatabaseSchema()
|
||||
{
|
||||
if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName)))
|
||||
@@ -393,8 +457,8 @@ namespace Umbraco.Core
|
||||
if (supportsCaseInsensitiveQueries == false)
|
||||
{
|
||||
message = "<p> </p><p>The database you're trying to use does not support case insensitive queries. <br />We currently do not support these types of databases.</p>" +
|
||||
"<p>You can fix this by changing the following two settings in your my.ini file in your MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1\nlower_case_file_system=1</pre><br />" +
|
||||
"<p>You can fix this by changing the following setting in your my.ini file in your MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
@@ -404,8 +468,8 @@ namespace Umbraco.Core
|
||||
else if (supportsCaseInsensitiveQueries == null)
|
||||
{
|
||||
message = "<p> </p><p>Warning! Could not check if your database type supports case insensitive queries. <br />We currently do not support these databases that do not support case insensitive queries.</p>" +
|
||||
"<p>You can check this by looking for the following two settings in your my.ini file in your MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1\nlower_case_file_system=1</pre><br />" +
|
||||
"<p>You can check this by looking for the following setting in your my.ini file in your MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
@@ -418,8 +482,8 @@ namespace Umbraco.Core
|
||||
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
|
||||
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
|
||||
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>They can check this by looking for the following two settings in the my.ini file in their MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1\nlower_case_file_system=1</pre><br />" +
|
||||
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
}
|
||||
@@ -446,6 +510,8 @@ namespace Umbraco.Core
|
||||
message = message + "<p>Upgrade completed!</p>";
|
||||
}
|
||||
|
||||
//now that everything is done, we need to determine the version of SQL server that is executing
|
||||
|
||||
LogHelper.Info<DatabaseContext>("Database configuration status: " + message);
|
||||
|
||||
return new Result { Message = message, Success = true, Percentage = "100" };
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DateTime as an ISO formatted string that is globally expectable
|
||||
/// </summary>
|
||||
/// <param name="dt"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToIsoString(this DateTime dt)
|
||||
{
|
||||
return dt.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class RecycleBinEventArgs : CancellableEventArgs
|
||||
{
|
||||
public RecycleBinEventArgs(Guid nodeObjectType, IEnumerable<int> ids, List<string> files, bool emptiedSuccessfully)
|
||||
: base(false)
|
||||
{
|
||||
NodeObjectType = nodeObjectType;
|
||||
Ids = ids;
|
||||
Files = files;
|
||||
RecycleBinEmptiedSuccessfully = emptiedSuccessfully;
|
||||
}
|
||||
|
||||
public RecycleBinEventArgs(Guid nodeObjectType, IEnumerable<int> ids, List<string> files)
|
||||
: base(true)
|
||||
{
|
||||
NodeObjectType = nodeObjectType;
|
||||
Ids = ids;
|
||||
Files = files;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Id of the node object type of the items
|
||||
/// being deleted from the Recycle Bin.
|
||||
/// </summary>
|
||||
public Guid NodeObjectType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Ids for each of the items being deleted from the Recycle Bin.
|
||||
/// </summary>
|
||||
public IEnumerable<int> Ids { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Files that should be deleted as part
|
||||
/// of emptying the Recycle Bin.
|
||||
/// </summary>
|
||||
public List<string> Files { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the Recycle Bin was emptied successfully
|
||||
/// </summary>
|
||||
public bool RecycleBinEmptiedSuccessfully { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether this event was fired for the Content's Recycle Bin.
|
||||
/// </summary>
|
||||
public bool IsContentRecycleBin
|
||||
{
|
||||
get { return NodeObjectType == new Guid(Constants.ObjectTypes.Document); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether this event was fired for the Media's Recycle Bin.
|
||||
/// </summary>
|
||||
public bool IsMediaRecycleBin
|
||||
{
|
||||
get { return NodeObjectType == new Guid(Constants.ObjectTypes.Media); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
if (virtualPath.StartsWith("~"))
|
||||
return virtualPath.Replace("~", SystemDirectories.Root).Replace("//", "/");
|
||||
else if (Uri.IsWellFormedUriString(virtualPath, UriKind.Absolute))
|
||||
return virtualPath;
|
||||
else
|
||||
return VirtualPathUtility.ToAbsolute(virtualPath, SystemDirectories.Root);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
internal class ResizedImage
|
||||
{
|
||||
public ResizedImage()
|
||||
{
|
||||
}
|
||||
|
||||
public ResizedImage(int width, int height, string fileName)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
FileName = fileName;
|
||||
}
|
||||
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public string FileName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
public class UmbracoMediaFile
|
||||
{
|
||||
private readonly MediaFileSystem _fs;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public UmbracoMediaFile()
|
||||
{
|
||||
_fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
}
|
||||
|
||||
public UmbracoMediaFile(string path)
|
||||
{
|
||||
_fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
|
||||
Path = path;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
|
||||
//MB: Do we really need all these overloads? looking through the code, only one of them is actually used
|
||||
|
||||
public static UmbracoMediaFile Save(HttpPostedFile file, string path)
|
||||
{
|
||||
return Save(file.InputStream, path);
|
||||
}
|
||||
|
||||
public static UmbracoMediaFile Save(HttpPostedFileBase file, string path)
|
||||
{
|
||||
return Save(file.InputStream, path);
|
||||
}
|
||||
|
||||
public static UmbracoMediaFile Save(Stream inputStream, string path)
|
||||
{
|
||||
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
fs.AddFile(path, inputStream);
|
||||
|
||||
return new UmbracoMediaFile(path);
|
||||
}
|
||||
|
||||
public static UmbracoMediaFile Save(byte[] file, string relativePath)
|
||||
{
|
||||
return Save(new MemoryStream(file), relativePath);
|
||||
}
|
||||
|
||||
public static UmbracoMediaFile Save(HttpPostedFile file)
|
||||
{
|
||||
var tempDir = System.IO.Path.Combine("uploads", Guid.NewGuid().ToString());
|
||||
return Save(file, tempDir);
|
||||
}
|
||||
|
||||
//filebase overload...
|
||||
public static UmbracoMediaFile Save(HttpPostedFileBase file)
|
||||
{
|
||||
var tempDir = System.IO.Path.Combine("uploads", Guid.NewGuid().ToString());
|
||||
return Save(file, tempDir);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private long? _length;
|
||||
private Size? _size;
|
||||
|
||||
/// <summary>
|
||||
/// Initialized values that don't require opening the file.
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
Filename = _fs.GetFileName(Path);
|
||||
Extension = _fs.GetExtension(Path) != null
|
||||
? _fs.GetExtension(Path).Substring(1).ToLowerInvariant()
|
||||
: "";
|
||||
Url = _fs.GetUrl(Path);
|
||||
}
|
||||
|
||||
public string Filename { get; private set; }
|
||||
|
||||
public string Extension { get; private set; }
|
||||
|
||||
public string Path { get; private set; }
|
||||
|
||||
public string Url { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the length of the file in bytes
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We are lazy loading this, don't go opening the file on ctor like we were doing.
|
||||
/// </remarks>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_length == null)
|
||||
{
|
||||
_length = _fs.GetSize(Path);
|
||||
}
|
||||
return _length.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SupportsResizing
|
||||
{
|
||||
get
|
||||
{
|
||||
return ("," + UmbracoSettings.ImageFileTypes + ",").Contains(string.Format(",{0},", Extension));
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFriendlyName()
|
||||
{
|
||||
return Filename.SplitPascalCasing().ToFirstUpperInvariant();
|
||||
}
|
||||
|
||||
public Size GetDimensions()
|
||||
{
|
||||
if (_size == null)
|
||||
{
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
var fs = _fs.OpenFile(Path);
|
||||
var image = Image.FromStream(fs);
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
fs.Close();
|
||||
image.Dispose();
|
||||
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
}
|
||||
return _size.Value;
|
||||
}
|
||||
|
||||
public string Resize(int width, int height)
|
||||
{
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
var fileNameThumb = DoResize(width, height, 0, string.Empty);
|
||||
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
}
|
||||
|
||||
public string Resize(int maxWidthHeight, string fileNameAddition)
|
||||
{
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
var fileNameThumb = DoResize(GetDimensions().Width, GetDimensions().Height, maxWidthHeight, fileNameAddition);
|
||||
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
}
|
||||
|
||||
private string DoResize(int width, int height, int maxWidthHeight, string fileNameAddition)
|
||||
{
|
||||
using (var fs = _fs.OpenFile(Path))
|
||||
{
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
|
||||
var thumbnail = GenerateThumbnail(image, maxWidthHeight, width, height, fileNameThumb, maxWidthHeight == 0);
|
||||
|
||||
return thumbnail.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFileSupportsResizing()
|
||||
{
|
||||
if (SupportsResizing == false)
|
||||
throw new InvalidOperationException(string.Format("The file {0} is not an image, so can't get dimensions", Filename));
|
||||
}
|
||||
|
||||
private ResizedImage GenerateThumbnail(Image image, int maxWidthHeight, int fileWidth, int fileHeight, string thumbnailFileName, bool useFixedDimensions)
|
||||
{
|
||||
// Generate thumbnail
|
||||
float f = 1;
|
||||
if (useFixedDimensions == false)
|
||||
{
|
||||
var fx = (float)image.Size.Width / (float)maxWidthHeight;
|
||||
var fy = (float)image.Size.Height / (float)maxWidthHeight;
|
||||
|
||||
// must fit in thumbnail size
|
||||
f = Math.Max(fx, fy);
|
||||
}
|
||||
|
||||
var widthTh = (int)Math.Round((float)fileWidth / f);
|
||||
var heightTh = (int)Math.Round((float)fileHeight / f);
|
||||
|
||||
// fixes for empty width or height
|
||||
if (widthTh == 0)
|
||||
widthTh = 1;
|
||||
if (heightTh == 0)
|
||||
heightTh = 1;
|
||||
|
||||
// Create new image with best quality settings
|
||||
using (var bp = new Bitmap(widthTh, heightTh))
|
||||
{
|
||||
using (var g = Graphics.FromImage(bp))
|
||||
{
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
// Copy the old image to the new and resized
|
||||
var rect = new Rectangle(0, 0, widthTh, heightTh);
|
||||
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
|
||||
|
||||
// Copy metadata
|
||||
var imageEncoders = ImageCodecInfo.GetImageEncoders();
|
||||
|
||||
var codec = Extension.ToLower() == "png" || Extension.ToLower() == "gif"
|
||||
? imageEncoders.Single(t => t.MimeType.Equals("image/png"))
|
||||
: imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
|
||||
|
||||
// Set compresion ratio to 90%
|
||||
var ep = new EncoderParameters();
|
||||
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
|
||||
|
||||
// Save the new image using the dimensions of the image
|
||||
var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
|
||||
_fs.AddFile(newFileName, ms);
|
||||
}
|
||||
|
||||
return new ResizedImage(widthTh, heightTh, newFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Models
|
||||
private DateTime? _expireDate;
|
||||
private int _writer;
|
||||
private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name.
|
||||
|
||||
private bool _permissionsChanged;
|
||||
/// <summary>
|
||||
/// Constructor for creating a Content object
|
||||
/// </summary>
|
||||
@@ -82,6 +82,7 @@ namespace Umbraco.Core.Models
|
||||
private static readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ExpireDate);
|
||||
private static readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo<Content, int>(x => x.WriterId);
|
||||
private static readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo<Content, string>(x => x.NodeName);
|
||||
private static readonly PropertyInfo PermissionsChangedSelector = ExpressionHelper.GetPropertyInfo<Content, bool>(x => x.PermissionsChanged);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template used by the Content.
|
||||
@@ -138,7 +139,10 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Boolean indicating whether this Content is Published or not
|
||||
/// </summary>
|
||||
/// <remarks>Setting Published to true/false should be private or internal</remarks>
|
||||
/// <remarks>
|
||||
/// Setting Published to true/false should be private or internal and should ONLY be used for wiring up the value
|
||||
/// from the db or modifying it based on changing the published state.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public bool Published
|
||||
{
|
||||
@@ -243,6 +247,22 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to track if permissions have been changed during the saving process for this entity
|
||||
/// </summary>
|
||||
internal bool PermissionsChanged
|
||||
{
|
||||
get { return _permissionsChanged; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_permissionsChanged = value;
|
||||
return _permissionsChanged;
|
||||
}, _permissionsChanged, PermissionsChangedSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
@@ -355,11 +375,25 @@ namespace Umbraco.Core.Models
|
||||
/// <returns>True if entity is dirty, otherwise False</returns>
|
||||
public override bool IsDirty()
|
||||
{
|
||||
bool dirtyEntity = base.IsDirty();
|
||||
return IsEntityDirty() || IsAnyUserPropertyDirty();
|
||||
}
|
||||
|
||||
bool dirtyProperties = Properties.Any(x => x.IsDirty());
|
||||
/// <summary>
|
||||
/// Returns true if only the entity properties are direty
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal bool IsEntityDirty()
|
||||
{
|
||||
return base.IsDirty();
|
||||
}
|
||||
|
||||
return dirtyEntity || dirtyProperties;
|
||||
/// <summary>
|
||||
/// Returns true if any of the properties are dirty
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal bool IsAnyUserPropertyDirty()
|
||||
{
|
||||
return Properties.Any(x => x.IsDirty());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Umbraco.Core.Models
|
||||
private bool _trashed;
|
||||
private int _contentTypeId;
|
||||
private PropertyCollection _properties;
|
||||
private readonly List<Property> _lastInvalidProperties = new List<Property>();
|
||||
|
||||
/// <summary>
|
||||
/// Protected constructor for ContentBase (Base for Content and Media)
|
||||
@@ -292,7 +293,10 @@ namespace Umbraco.Core.Models
|
||||
/// <returns><see cref="Property"/> Value as a <see cref="TPassType"/></returns>
|
||||
public virtual TPassType GetValue<TPassType>(string propertyTypeAlias)
|
||||
{
|
||||
return (TPassType)Properties[propertyTypeAlias].Value;
|
||||
if (Properties[propertyTypeAlias].Value is TPassType)
|
||||
return (TPassType)Properties[propertyTypeAlias].Value;
|
||||
|
||||
return (TPassType)Convert.ChangeType(Properties[propertyTypeAlias].Value, typeof(TPassType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -421,9 +425,34 @@ namespace Umbraco.Core.Models
|
||||
/// <returns>True if content is valid otherwise false</returns>
|
||||
public virtual bool IsValid()
|
||||
{
|
||||
return Properties.Any(property => !property.IsValid()) == false;
|
||||
_lastInvalidProperties.Clear();
|
||||
_lastInvalidProperties.AddRange(Properties.Where(property => property.IsValid() == false));
|
||||
return _lastInvalidProperties.Any() == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a collection of the result of the last validation process, this collection contains all invalid properties.
|
||||
/// </summary>
|
||||
internal IEnumerable<Property> LastInvalidProperties
|
||||
{
|
||||
get { return _lastInvalidProperties; }
|
||||
}
|
||||
|
||||
public abstract void ChangeTrashedState(bool isTrashed, int parentId = -20);
|
||||
|
||||
/// <summary>
|
||||
/// We will override this method to ensure that when we reset the dirty properties that we
|
||||
/// also reset the dirty changes made to the content's Properties (user defined)
|
||||
/// </summary>
|
||||
/// <param name="rememberPreviouslyChangedProperties"></param>
|
||||
internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
|
||||
foreach (var prop in Properties)
|
||||
{
|
||||
prop.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Core.Persistence;
|
||||
@@ -23,6 +24,107 @@ namespace Umbraco.Core.Models
|
||||
public static class ContentExtensions
|
||||
{
|
||||
#region IContent
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a new version should be created
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// A new version needs to be created when:
|
||||
/// * The publish status is changed
|
||||
/// * The language is changed
|
||||
/// * The item is already published and is being published again and any property value is changed (to enable a rollback)
|
||||
/// </remarks>
|
||||
internal static bool ShouldCreateNewVersion(this IContent entity)
|
||||
{
|
||||
var publishedState = ((Content)entity).PublishedState;
|
||||
return ShouldCreateNewVersion(entity, publishedState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a new version should be created
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="publishedState"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// A new version needs to be created when:
|
||||
/// * The publish status is changed
|
||||
/// * The language is changed
|
||||
/// * The item is already published and is being published again and any property value is changed (to enable a rollback)
|
||||
/// </remarks>
|
||||
internal static bool ShouldCreateNewVersion(this IContent entity, PublishedState publishedState)
|
||||
{
|
||||
var dirtyEntity = (ICanBeDirty)entity;
|
||||
|
||||
//check if the published state has changed or the language
|
||||
var contentChanged =
|
||||
(dirtyEntity.IsPropertyDirty("Published") && publishedState != PublishedState.Unpublished)
|
||||
|| dirtyEntity.IsPropertyDirty("Language");
|
||||
|
||||
//return true if published or language has changed
|
||||
if (contentChanged)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//check if any user prop has changed
|
||||
var propertyValueChanged = ((Content) entity).IsAnyUserPropertyDirty();
|
||||
//check if any content prop has changed
|
||||
var contentDataChanged = ((Content) entity).IsEntityDirty();
|
||||
|
||||
//return true if the item is published and a property has changed or if any content property has changed
|
||||
return (propertyValueChanged && publishedState == PublishedState.Published) || contentDataChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the published db flag should be set to true for the current entity version and all other db
|
||||
/// versions should have their flag set to false.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is determined by:
|
||||
/// * If a new version is being created and the entity is published
|
||||
/// * If the published state has changed and the entity is published OR the entity has been un-published.
|
||||
/// </remarks>
|
||||
internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity)
|
||||
{
|
||||
var publishedState = ((Content)entity).PublishedState;
|
||||
return entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, entity.ShouldCreateNewVersion(publishedState));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the published db flag should be set to true for the current entity version and all other db
|
||||
/// versions should have their flag set to false.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="publishedState"></param>
|
||||
/// <param name="isCreatingNewVersion"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is determined by:
|
||||
/// * If a new version is being created and the entity is published
|
||||
/// * If the published state has changed and the entity is published OR the entity has been un-published.
|
||||
/// </remarks>
|
||||
internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity, PublishedState publishedState, bool isCreatingNewVersion)
|
||||
{
|
||||
if (isCreatingNewVersion && entity.Published)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//If Published state has changed then previous versions should have their publish state reset.
|
||||
//If state has been changed to unpublished the previous versions publish state should also be reset.
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the current contents ancestors, not including the content itself.
|
||||
/// </summary>
|
||||
@@ -535,6 +637,11 @@ namespace Umbraco.Core.Models
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(media);
|
||||
}
|
||||
|
||||
internal static XElement ToDeepXml(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(media, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
|
||||
@@ -181,6 +181,38 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a clone of the current entity
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContentType Clone(string alias)
|
||||
{
|
||||
var clone = (ContentType)this.MemberwiseClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
var propertyGroups = this.PropertyGroups.Select(x => x.Clone()).ToList();
|
||||
clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
|
||||
clone.PropertyTypes = this.PropertyTypeCollection.Select(x => x.Clone()).ToList();
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
foreach (var propertyType in propertyGroup.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyType in clone.PropertyTypes.Where(x => x.HasIdentity))
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
|
||||
@@ -494,5 +494,14 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_parentId = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PropertyTypes that are not part of a PropertyGroup
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
internal PropertyTypeCollection PropertyTypeCollection
|
||||
{
|
||||
get { return _propertyTypes; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// Please note that resetting the dirty properties could potentially
|
||||
/// obstruct the saving of a new or updated entity.
|
||||
/// </remarks>
|
||||
internal void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
internal virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
if (rememberPreviouslyChangedProperties)
|
||||
{
|
||||
|
||||
@@ -7,17 +7,25 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// Defines the interface for a <see cref="User"/>
|
||||
/// </summary>
|
||||
/// <remarks>Will be left internal until a proper Membership implementation is part of the roadmap</remarks>
|
||||
internal interface IUser : IMembershipUser
|
||||
internal interface IUser : IMembershipUser, IUserProfile
|
||||
{
|
||||
string Name { get; set; }
|
||||
new object Id { get; set; }
|
||||
//string Name { get; set; }
|
||||
int SessionTimeout { get; set; }
|
||||
int StartContentId { get; set; }
|
||||
int StartMediaId { get; set; }
|
||||
string Language { get; set; }
|
||||
bool DefaultToLiveEditing { get; set; }
|
||||
IEnumerable<string> Applications { get; set; }
|
||||
|
||||
bool NoConsole { get; set; }
|
||||
IUserType UserType { get; }
|
||||
string Permissions { get; set; }
|
||||
string DefaultPermissions { get; set; }
|
||||
}
|
||||
|
||||
internal interface IUserProfile : IProfile
|
||||
{
|
||||
IEnumerable<string> AllowedSections { get; }
|
||||
void RemoveAllowedSection(string sectionAlias);
|
||||
void AddAllowedSection(string sectionAlias);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
@@ -8,7 +10,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class Profile : IProfile
|
||||
internal class Profile : TracksChangesEntityBase, IProfile, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Profile"/> class.
|
||||
@@ -25,11 +27,47 @@ namespace Umbraco.Core.Models.Membership
|
||||
Name = name;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public object Id { get; set; }
|
||||
private object _id;
|
||||
private string _name;
|
||||
private Type _userTypeKey;
|
||||
|
||||
private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo<Profile, object>(x => x.Id);
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<Profile, string>(x => x.Name);
|
||||
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo<Profile, Type>(x => x.ProviderUserKeyType);
|
||||
|
||||
[DataMember]
|
||||
public string Name { get; set; }
|
||||
public object Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_id = value;
|
||||
return _id;
|
||||
}, _id, IdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_name = value;
|
||||
return _name;
|
||||
}, _name, NameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public virtual object ProviderUserKey
|
||||
@@ -45,7 +83,21 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// The type of the provider user key.
|
||||
/// </value>
|
||||
[IgnoreDataMember]
|
||||
internal Type ProviderUserKeyType { get; private set; }
|
||||
internal Type ProviderUserKeyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userTypeKey;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_userTypeKey = value;
|
||||
return _userTypeKey;
|
||||
}, _userTypeKey, UserTypeKeySelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the provider user key.
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
|
||||
public User(IUserType userType)
|
||||
{
|
||||
Groups = new List<object> {userType};
|
||||
Groups = new List<object> { userType };
|
||||
}
|
||||
|
||||
#region Implementation of IEntity
|
||||
@@ -91,7 +91,8 @@ namespace Umbraco.Core.Models.Membership
|
||||
[DataMember]
|
||||
public string Language { get; set; }
|
||||
[DataMember]
|
||||
public string Permissions { get; set; }
|
||||
public string DefaultPermissions { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public bool DefaultToLiveEditing { get; set; }
|
||||
[DataMember]
|
||||
@@ -100,8 +101,8 @@ namespace Umbraco.Core.Models.Membership
|
||||
[IgnoreDataMember]
|
||||
public IUserType UserType
|
||||
{
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
var type = Groups.FirstOrDefault();
|
||||
return type as IUserType;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
@@ -14,14 +17,29 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UserProfile : Profile
|
||||
internal class UserProfile : Profile, IUserProfile
|
||||
{
|
||||
public UserProfile()
|
||||
{
|
||||
SessionTimeout = 60;
|
||||
Applications = Enumerable.Empty<string>();
|
||||
_sectionCollection = new ObservableCollection<string>();
|
||||
_addedSections = new List<string>();
|
||||
_removedSections = new List<string>();
|
||||
_sectionCollection.CollectionChanged += SectionCollectionChanged;
|
||||
}
|
||||
|
||||
private readonly List<string> _addedSections;
|
||||
private readonly List<string> _removedSections;
|
||||
private readonly ObservableCollection<string> _sectionCollection;
|
||||
private int _sessionTimeout;
|
||||
private int _startContentId;
|
||||
private int _startMediaId;
|
||||
|
||||
private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<UserProfile, int>(x => x.SessionTimeout);
|
||||
private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<UserProfile, int>(x => x.StartContentId);
|
||||
private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<UserProfile, int>(x => x.StartMediaId);
|
||||
private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo<UserProfile, IEnumerable<string>>(x => x.AllowedSections);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the session timeout.
|
||||
/// </summary>
|
||||
@@ -29,7 +47,21 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// The session timeout.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int SessionTimeout { get; set; }
|
||||
public int SessionTimeout
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sessionTimeout;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_sessionTimeout = value;
|
||||
return _sessionTimeout;
|
||||
}, _sessionTimeout, SessionTimeoutSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start content id.
|
||||
@@ -38,7 +70,21 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// The start content id.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int StartContentId { get; set; }
|
||||
public int StartContentId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startContentId;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_startContentId = value;
|
||||
return _startContentId;
|
||||
}, _startContentId, StartContentIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start media id.
|
||||
@@ -47,15 +93,96 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// The start media id.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int StartMediaId { get; set; }
|
||||
public int StartMediaId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startMediaId;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_startMediaId = value;
|
||||
return _startMediaId;
|
||||
}, _startMediaId, StartMediaIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllowedSections
|
||||
{
|
||||
get { return _sectionCollection; }
|
||||
}
|
||||
|
||||
public void RemoveAllowedSection(string sectionAlias)
|
||||
{
|
||||
_sectionCollection.Remove(sectionAlias);
|
||||
}
|
||||
|
||||
public void AddAllowedSection(string sectionAlias)
|
||||
{
|
||||
if (!_sectionCollection.Contains(sectionAlias))
|
||||
{
|
||||
_sectionCollection.Add(sectionAlias);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the applications.
|
||||
/// Whenever resetting occurs, clear the remembered add/removed collections, even if
|
||||
/// rememberPreviouslyChangedProperties is true, the AllowedSections property will still
|
||||
/// be flagged as dirty.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The applications.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public IEnumerable<string> Applications { get; set; }
|
||||
/// <param name="rememberPreviouslyChangedProperties"></param>
|
||||
internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
_addedSections.Clear();
|
||||
_removedSections.Clear();
|
||||
base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to check if we need to add a section in the repository to the db
|
||||
/// </summary>
|
||||
internal IEnumerable<string> AddedSections
|
||||
{
|
||||
get { return _addedSections; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to check if we need to remove a section in the repository to the db
|
||||
/// </summary>
|
||||
internal IEnumerable<string> RemovedSections
|
||||
{
|
||||
get { return _removedSections; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the collection changed event in order for us to flag the AllowedSections property as changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(AllowedSectionsSelector);
|
||||
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
//remove from the removed/added sections (since people could add/remove all they want in one request)
|
||||
_removedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
|
||||
_addedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
|
||||
|
||||
//add to the added sections
|
||||
_addedSections.Add(e.NewItems.Cast<string>().First());
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
//remove from the removed/added sections (since people could add/remove all they want in one request)
|
||||
_removedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
|
||||
_addedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
|
||||
|
||||
//add to the added sections
|
||||
_removedSections.Add(e.OldItems.Cast<string>().First());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Services;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -14,10 +22,15 @@ namespace Umbraco.Core.Models
|
||||
/// <returns>Xml of the property and its value</returns>
|
||||
public static XElement ToXml(this Property property)
|
||||
{
|
||||
string nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias();
|
||||
return property.ToXml(ApplicationContext.Current.Services.DataTypeService);
|
||||
}
|
||||
|
||||
internal static XElement ToXml(this Property property, IDataTypeService dataTypeService)
|
||||
{
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias();
|
||||
|
||||
var xd = new XmlDocument();
|
||||
XmlNode xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, "");
|
||||
var xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, "");
|
||||
|
||||
//Add the property alias to the legacy schema
|
||||
if (UmbracoSettings.UseLegacyXmlSchema)
|
||||
@@ -28,8 +41,21 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
//This seems to fail during testing
|
||||
xmlNode.AppendChild(property.PropertyType.DataType(property.Id).Data.ToXMl(xd));
|
||||
|
||||
//SD: With the new null checks below, this shouldn't fail anymore.
|
||||
var dt = property.PropertyType.DataType(property.Id, dataTypeService);
|
||||
if (dt != null && dt.Data != null)
|
||||
{
|
||||
//We've already got the value for the property so we're going to give it to the
|
||||
// data type's data property so it doesn't go re-look up the value from the db again.
|
||||
var defaultData = dt.Data as IDataValueSetter;
|
||||
if (defaultData != null)
|
||||
{
|
||||
defaultData.SetValue(property.Value, property.PropertyType.DataTypeDatabaseType.ToString());
|
||||
}
|
||||
|
||||
xmlNode.AppendChild(dt.Data.ToXMl(xd));
|
||||
}
|
||||
|
||||
var element = xmlNode.GetXElement();
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -108,6 +108,23 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
internal PropertyGroup Clone()
|
||||
{
|
||||
var clone = (PropertyGroup) this.MemberwiseClone();
|
||||
var collection = new PropertyTypeCollection();
|
||||
foreach (var propertyType in this.PropertyTypes)
|
||||
{
|
||||
var property = propertyType.Clone();
|
||||
property.ResetIdentity();
|
||||
property.ResetDirtyProperties(false);
|
||||
collection.Add(property);
|
||||
}
|
||||
clone.PropertyTypes = collection;
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ParentId from the lazy integer id
|
||||
/// </summary>
|
||||
|
||||
@@ -378,6 +378,14 @@ namespace Umbraco.Core.Models
|
||||
return true;
|
||||
}
|
||||
|
||||
internal PropertyType Clone()
|
||||
{
|
||||
var clone = (PropertyType) this.MemberwiseClone();
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public bool Equals(PropertyType other)
|
||||
{
|
||||
//Check whether the compared object is null.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Services;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -9,6 +10,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="propertyType">PropertyType that references a DataType</param>
|
||||
/// <param name="propertyId">Id of the Property which references this DataType through its PropertyType</param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <returns><see cref="IDataType"/></returns>
|
||||
/// <remarks>
|
||||
/// This extension method is left internal because we don't want to take
|
||||
@@ -16,10 +18,14 @@ namespace Umbraco.Core.Models
|
||||
/// be replaced by PropertyEditors. It is however needed to generate xml
|
||||
/// for a property/propertytype when publishing.
|
||||
/// </remarks>
|
||||
internal static IDataType DataType(this PropertyType propertyType, int propertyId)
|
||||
internal static IDataType DataType(this PropertyType propertyType, int propertyId, IDataTypeService dataTypeService)
|
||||
{
|
||||
Mandate.ParameterNotNull(propertyType, "propertyType");
|
||||
var dataType = ApplicationContext.Current.Services.DataTypeService.GetDataTypeById(propertyType.DataTypeId);
|
||||
var dataType = dataTypeService.GetDataTypeById(propertyType.DataTypeId);
|
||||
if (dataType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
dataType.DataTypeDefinitionId = propertyType.DataTypeDefinitionId;
|
||||
dataType.Data.PropertyId = propertyId;
|
||||
return dataType;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Core.Persistence;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Models.Rdbms
|
||||
@@ -59,5 +60,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("defaultToLiveEditing")]
|
||||
[Constraint(Default = "0")]
|
||||
public bool DefaultToLiveEditing { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
public List<User2AppDto> User2AppDtos { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ namespace Umbraco.Core.Models
|
||||
private int _level;
|
||||
private int _sortOrder;
|
||||
private int _parentId;
|
||||
private string _nodePath;
|
||||
private int _masterTemplateId;
|
||||
private string _masterTemplateAlias;
|
||||
|
||||
@@ -28,7 +27,6 @@ namespace Umbraco.Core.Models
|
||||
private static readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.Level);
|
||||
private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.SortOrder);
|
||||
private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.ParentId);
|
||||
private static readonly PropertyInfo NodePathSelector = ExpressionHelper.GetPropertyInfo<Template, string>(x => x.NodePath);
|
||||
//private static readonly PropertyInfo MasterTemplateIdSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.MasterTemplateId);
|
||||
private static readonly PropertyInfo MasterTemplateAliasSelector = ExpressionHelper.GetPropertyInfo<Template, string>(x => x.MasterTemplateAlias);
|
||||
|
||||
@@ -104,21 +102,7 @@ namespace Umbraco.Core.Models
|
||||
}, _parentId, ParentIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
internal string NodePath
|
||||
{
|
||||
get { return _nodePath; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_nodePath = value;
|
||||
return _nodePath;
|
||||
}, _nodePath, NodePathSelector);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[DataMember]
|
||||
internal Lazy<int> MasterTemplateId { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Currently just used to get the machine name in med trust and to format a machine name for use with file names
|
||||
/// </summary>
|
||||
internal class NetworkHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the machine name that is safe to use in file paths.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// see: https://github.com/Shandem/ClientDependency/issues/4
|
||||
/// </remarks>
|
||||
public static string FileSafeMachineName
|
||||
{
|
||||
get { return MachineName.ReplaceNonAlphanumericChars('-'); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current machine name
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Tries to resolve the machine name, if it cannot it uses the config section.
|
||||
/// </remarks>
|
||||
public static string MachineName
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return Environment.MachineName;
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
return System.Net.Dns.GetHostName();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//if we get here it means we cannot access the machine name
|
||||
throw new ApplicationException("Cannot resolve the current machine name eithe by Environment.MachineName or by Dns.GetHostname()");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Caching;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Caching
|
||||
@@ -11,6 +13,23 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
/// <summary>
|
||||
/// The Runtime Cache provider looks up objects in the Runtime cache for fast retrival
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// If a web session is detected then the HttpRuntime.Cache will be used for the runtime cache, otherwise a custom
|
||||
/// MemoryCache instance will be used. It is important to use the HttpRuntime.Cache when a web session is detected so
|
||||
/// that the memory management of cache in IIS can be handled appopriately.
|
||||
///
|
||||
/// When a web sessions is detected we will pre-fix all HttpRuntime.Cache entries so that when we clear it we are only
|
||||
/// clearing items that have been inserted by this provider.
|
||||
///
|
||||
/// NOTE: These changes are all temporary until we finalize the ApplicationCache implementation which will support static cache, runtime cache
|
||||
/// and request based cache which will all live in one central location so it is easily managed.
|
||||
///
|
||||
/// Also note that we don't always keep checking if HttpContext.Current == null and instead check for _memoryCache != null. This is because
|
||||
/// when there are async requests being made even in the context of a web request, the HttpContext.Current will be null but the HttpRuntime.Cache will
|
||||
/// always be available.
|
||||
///
|
||||
/// </remarks>
|
||||
internal sealed class RuntimeCacheProvider : IRepositoryCacheProvider
|
||||
{
|
||||
#region Singleton
|
||||
@@ -21,19 +40,25 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
private RuntimeCacheProvider()
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
{
|
||||
_memoryCache = new MemoryCache("in-memory");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
//TODO Save this in cache as well, so its not limited to a single server usage
|
||||
private readonly ConcurrentHashSet<string> _keyTracker = new ConcurrentHashSet<string>();
|
||||
private ObjectCache _memoryCache = new MemoryCache("in-memory");
|
||||
private ObjectCache _memoryCache;
|
||||
private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim();
|
||||
|
||||
public IEntity GetById(Type type, Guid id)
|
||||
{
|
||||
var key = GetCompositeId(type, id);
|
||||
var item = _memoryCache.Get(key);
|
||||
var item = _memoryCache != null
|
||||
? _memoryCache.Get(key)
|
||||
: HttpRuntime.Cache.Get(key);
|
||||
return item as IEntity;
|
||||
}
|
||||
|
||||
@@ -41,7 +66,11 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
{
|
||||
foreach (var guid in ids)
|
||||
{
|
||||
yield return _memoryCache.Get(GetCompositeId(type, guid)) as IEntity;
|
||||
var item = _memoryCache != null
|
||||
? _memoryCache.Get(GetCompositeId(type, guid))
|
||||
: HttpRuntime.Cache.Get(GetCompositeId(type, guid));
|
||||
|
||||
yield return item as IEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +78,13 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
{
|
||||
foreach (var key in _keyTracker)
|
||||
{
|
||||
if (key.StartsWith(type.Name))
|
||||
if (key.StartsWith(string.Format("{0}{1}-", CacheItemPrefix, type.Name)))
|
||||
{
|
||||
yield return _memoryCache.Get(key) as IEntity;
|
||||
var item = _memoryCache != null
|
||||
? _memoryCache.Get(key)
|
||||
: HttpRuntime.Cache.Get(key);
|
||||
|
||||
yield return item as IEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,21 +92,34 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
public void Save(Type type, IEntity entity)
|
||||
{
|
||||
var key = GetCompositeId(type, entity.Id);
|
||||
var exists = _memoryCache.GetCacheItem(key) != null;
|
||||
|
||||
_keyTracker.TryAdd(key);
|
||||
if (exists)
|
||||
|
||||
//NOTE: Before we were checking if it already exists but the MemoryCache.Set handles this implicitly and does
|
||||
// an add or update, same goes for HttpRuntime.Cache.Insert.
|
||||
|
||||
if (_memoryCache != null)
|
||||
{
|
||||
_memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
|
||||
return;
|
||||
}
|
||||
|
||||
_memoryCache.Add(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
|
||||
else
|
||||
{
|
||||
HttpRuntime.Cache.Insert(key, entity, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(Type type, IEntity entity)
|
||||
{
|
||||
var key = GetCompositeId(type, entity.Id);
|
||||
_memoryCache.Remove(key);
|
||||
if (_memoryCache != null)
|
||||
{
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpRuntime.Cache.Remove(key);
|
||||
}
|
||||
|
||||
_keyTracker.Remove(key);
|
||||
}
|
||||
|
||||
@@ -88,14 +134,21 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
var keys = new string[_keyTracker.Count];
|
||||
_keyTracker.CopyTo(keys, 0);
|
||||
var keysToRemove = new List<string>();
|
||||
foreach (var key in keys.Where(x => x.StartsWith(string.Format("{0}-", type.Name))))
|
||||
foreach (var key in keys.Where(x => x.StartsWith(string.Format("{0}{1}-", CacheItemPrefix, type.Name))))
|
||||
{
|
||||
_keyTracker.Remove(key);
|
||||
keysToRemove.Add(key);
|
||||
}
|
||||
foreach (var key in keysToRemove)
|
||||
{
|
||||
_memoryCache.Remove(key);
|
||||
if (_memoryCache != null)
|
||||
{
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpRuntime.Cache.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,19 +158,40 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
using (new WriteLock(ClearLock))
|
||||
{
|
||||
_keyTracker.Clear();
|
||||
_memoryCache.DisposeIfDisposable();
|
||||
_memoryCache = new MemoryCache("in-memory");
|
||||
|
||||
if (_memoryCache != null)
|
||||
{
|
||||
_memoryCache.DisposeIfDisposable();
|
||||
_memoryCache = new MemoryCache("in-memory");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DictionaryEntry c in HttpRuntime.Cache)
|
||||
{
|
||||
if (c.Key is string && ((string)c.Key).InvariantStartsWith(CacheItemPrefix))
|
||||
{
|
||||
if (HttpRuntime.Cache[(string)c.Key] == null) return;
|
||||
HttpRuntime.Cache.Remove((string)c.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We prefix all cache keys with this so that we know which ones this class has created when
|
||||
/// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create.
|
||||
/// </summary>
|
||||
private const string CacheItemPrefix = "umbrtmche_";
|
||||
|
||||
private string GetCompositeId(Type type, Guid id)
|
||||
{
|
||||
return string.Format("{0}-{1}", type.Name, id.ToString());
|
||||
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id.ToString());
|
||||
}
|
||||
|
||||
private string GetCompositeId(Type type, int id)
|
||||
{
|
||||
return string.Format("{0}-{1}", type.Name, id.ToGuid());
|
||||
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id.ToGuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Data;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
|
||||
{
|
||||
@@ -107,6 +108,12 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
|
||||
var constraintAttribute = propertyInfo.FirstAttribute<ConstraintAttribute>();
|
||||
if (constraintAttribute != null)
|
||||
{
|
||||
//Special case for MySQL as it can't have multiple default DateTime values, which
|
||||
//is what the umbracoServer table definition is trying to create
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider is MySqlSyntaxProvider && definition.TableName == "umbracoServer" &&
|
||||
definition.TableName.ToLowerInvariant() == "lastNotifiedDate".ToLowerInvariant())
|
||||
return definition;
|
||||
|
||||
definition.ConstraintName = constraintAttribute.Name ?? string.Empty;
|
||||
definition.DefaultValue = constraintAttribute.Default ?? string.Empty;
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Umbraco.Core.Persistence
|
||||
/// </remarks>
|
||||
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory
|
||||
{
|
||||
private readonly string _connectionStringName;
|
||||
private readonly string _connectionString;
|
||||
private readonly string _providerName;
|
||||
private readonly string _connectionStringName;
|
||||
public string ConnectionString { get; private set; }
|
||||
public string ProviderName { get; private set; }
|
||||
|
||||
//very important to have ThreadStatic:
|
||||
// see: http://issues.umbraco.org/issue/U4-2172
|
||||
@@ -52,8 +52,8 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
Mandate.ParameterNotNullOrEmpty(connectionString, "connectionString");
|
||||
Mandate.ParameterNotNullOrEmpty(providerName, "providerName");
|
||||
_connectionString = connectionString;
|
||||
_providerName = providerName;
|
||||
ConnectionString = connectionString;
|
||||
ProviderName = providerName;
|
||||
}
|
||||
|
||||
public UmbracoDatabase CreateDatabase()
|
||||
@@ -68,8 +68,8 @@ namespace Umbraco.Core.Persistence
|
||||
//double check
|
||||
if (_nonHttpInstance == null)
|
||||
{
|
||||
_nonHttpInstance = string.IsNullOrEmpty(_connectionString) == false && string.IsNullOrEmpty(_providerName) == false
|
||||
? new UmbracoDatabase(_connectionString, _providerName)
|
||||
_nonHttpInstance = string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
|
||||
? new UmbracoDatabase(ConnectionString, ProviderName)
|
||||
: new UmbracoDatabase(_connectionStringName);
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,8 @@ namespace Umbraco.Core.Persistence
|
||||
if (HttpContext.Current.Items.Contains(typeof(DefaultDatabaseFactory)) == false)
|
||||
{
|
||||
HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory),
|
||||
string.IsNullOrEmpty(_connectionString) == false && string.IsNullOrEmpty(_providerName) == false
|
||||
? new UmbracoDatabase(_connectionString, _providerName)
|
||||
string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
|
||||
? new UmbracoDatabase(ConnectionString, ProviderName)
|
||||
: new UmbracoDatabase(_connectionStringName));
|
||||
}
|
||||
return (UmbracoDatabase)HttpContext.Current.Items[typeof(DefaultDatabaseFactory)];
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
Level = dto.NodeDto.Level,
|
||||
ParentId = dto.NodeDto.ParentId,
|
||||
SortOrder = dto.NodeDto.SortOrder,
|
||||
NodePath = dto.NodeDto.Path
|
||||
Path = dto.NodeDto.Path
|
||||
};
|
||||
|
||||
if(dto.Master.HasValue)
|
||||
@@ -83,7 +83,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
|
||||
NodeObjectType = _nodeObjectTypeId,
|
||||
ParentId = entity.ParentId,
|
||||
Path = entity.NodePath,
|
||||
Path = entity.Path,
|
||||
SortOrder = entity.SortOrder,
|
||||
Text = entity.Name,
|
||||
Trashed = false,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
@@ -16,8 +17,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
public IUser BuildEntity(UserDto dto)
|
||||
{
|
||||
//TODO Add list of applications for user
|
||||
return new User(_userType)
|
||||
var user = new User(_userType)
|
||||
{
|
||||
Id = dto.Id,
|
||||
ProfileId = dto.Id,
|
||||
@@ -32,8 +32,19 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
Language = dto.UserLanguage,
|
||||
DefaultToLiveEditing = dto.DefaultToLiveEditing,
|
||||
NoConsole = dto.NoConsole,
|
||||
Permissions = dto.DefaultPermissions
|
||||
DefaultPermissions = dto.DefaultPermissions
|
||||
};
|
||||
|
||||
foreach (var app in dto.User2AppDtos)
|
||||
{
|
||||
user.AddAllowedSection(app.AppAlias);
|
||||
}
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
user.ResetDirtyProperties(false);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public UserDto BuildDto(IUser entity)
|
||||
@@ -51,9 +62,24 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
UserLanguage = entity.Language,
|
||||
UserName = entity.Name,
|
||||
Type = short.Parse(entity.UserType.Id.ToString()),
|
||||
DefaultPermissions = entity.Permissions
|
||||
DefaultPermissions = entity.DefaultPermissions,
|
||||
User2AppDtos = new List<User2AppDto>()
|
||||
};
|
||||
|
||||
foreach (var app in entity.AllowedSections)
|
||||
{
|
||||
var appDto = new User2AppDto
|
||||
{
|
||||
AppAlias = app
|
||||
};
|
||||
if (entity.Id != null)
|
||||
{
|
||||
appDto.UserId = (int) entity.Id;
|
||||
}
|
||||
|
||||
dto.User2AppDtos.Add(appDto);
|
||||
}
|
||||
|
||||
if (entity.HasIdentity)
|
||||
dto.Id = entity.Id.SafeCast<int>();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
@@ -7,11 +8,24 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
public abstract class BaseMapper
|
||||
{
|
||||
|
||||
internal abstract ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache { get; }
|
||||
|
||||
internal abstract void BuildMap();
|
||||
|
||||
internal abstract string Map(string propertyName);
|
||||
internal string Map(string propertyName)
|
||||
{
|
||||
DtoMapModel dtoTypeProperty;
|
||||
return PropertyInfoCache.TryGetValue(propertyName, out dtoTypeProperty)
|
||||
? GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo)
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
internal abstract void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember);
|
||||
internal void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
internal DtoMapModel ResolveMapping<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IContent))]
|
||||
public sealed class ContentMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
if(PropertyInfoCache.IsEmpty)
|
||||
@@ -52,23 +57,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
//CacheMap<Content, DocumentDto>(src => src.Template, dto => dto.TemplateId);
|
||||
}
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IContentType))]
|
||||
public sealed class ContentTypeMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
if (PropertyInfoCache.IsEmpty)
|
||||
@@ -48,22 +53,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
}
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IDataTypeDefinition))]
|
||||
public sealed class DataTypeDefinitionMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<DataTypeDefinition, NodeDto>(src => src.Id, dto => dto.NodeId);
|
||||
@@ -42,22 +47,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IDictionaryItem))]
|
||||
public sealed class DictionaryMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<DictionaryItem, DictionaryDto>(src => src.Id, dto => dto.PrimaryKey);
|
||||
@@ -32,23 +37,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<DictionaryItem, DictionaryDto>(src => src.ItemKey, dto => dto.Key);
|
||||
CacheMap<DictionaryItem, DictionaryDto>(src => src.ParentId, dto => dto.Parent);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IDictionaryTranslation))]
|
||||
public class DictionaryTranslationMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<DictionaryTranslation, LanguageTextDto>(src => src.Id, dto => dto.PrimaryKey);
|
||||
@@ -33,22 +38,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<DictionaryTranslation, LanguageTextDto>(src => src.Value, dto => dto.Value);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Language))]
|
||||
public sealed class LanguageMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<Language, LanguageDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -32,22 +37,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<Language, LanguageDto>(src => src.CultureName, dto => dto.CultureName);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Umbraco.Core.Models.Media))]
|
||||
public sealed class MediaMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
if (PropertyInfoCache.IsEmpty)
|
||||
@@ -45,22 +50,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
}
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(MediaType))]
|
||||
public sealed class MediaTypeMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
if (PropertyInfoCache.IsEmpty)
|
||||
@@ -48,22 +53,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
}
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(PropertyGroup))]
|
||||
public sealed class PropertyGroupMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -24,6 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<PropertyGroup, PropertyTypeGroupDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -32,22 +37,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<PropertyGroup, PropertyTypeGroupDto>(src => src.Name, dto => dto.Text);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Property))]
|
||||
public sealed class PropertyMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -20,6 +20,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<Property, PropertyDataDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -27,22 +32,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<Property, PropertyDataDto>(src => src.PropertyTypeId, dto => dto.PropertyTypeId);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(PropertyType))]
|
||||
public sealed class PropertyTypeMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -24,6 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
if(PropertyInfoCache.IsEmpty)
|
||||
@@ -42,22 +47,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
}
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Relation))]
|
||||
public sealed class RelationMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -24,6 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<Relation, RelationDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -33,23 +38,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<Relation, RelationDto>(src => src.ParentId, dto => dto.ParentId);
|
||||
CacheMap<Relation, RelationDto>(src => src.RelationTypeId, dto => dto.RelationType);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(RelationType))]
|
||||
public sealed class RelationTypeMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -24,6 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<RelationType, RelationTypeDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -33,23 +38,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<RelationType, RelationTypeDto>(src => src.Name, dto => dto.Name);
|
||||
CacheMap<RelationType, RelationTypeDto>(src => src.ParentObjectType, dto => dto.ParentObjectType);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(ServerRegistration))]
|
||||
public sealed class ServerRegistrationMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -20,6 +20,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -30,22 +35,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.ComputerName, dto => dto.ComputerName);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof (IUmbracoEntity))]
|
||||
public sealed class UmbracoEntityMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache =
|
||||
new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -21,6 +20,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<IUmbracoEntity, NodeDto>(src => src.Id, dto => dto.NodeId);
|
||||
@@ -34,24 +38,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<IUmbracoEntity, NodeDto>(src => src.Key, dto => dto.UniqueId);
|
||||
CacheMap<IUmbracoEntity, NodeDto>(src => src.CreatorId, dto => dto.UserId);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember,
|
||||
Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(User))]
|
||||
public sealed class UserMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -21,6 +21,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<User, UserDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -28,7 +33,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<User, UserDto>(src => src.Username, dto => dto.Login);
|
||||
CacheMap<User, UserDto>(src => src.Password, dto => dto.Password);
|
||||
CacheMap<User, UserDto>(src => src.Name, dto => dto.UserName);
|
||||
CacheMap<User, UserDto>(src => src.Permissions, dto => dto.DefaultPermissions);
|
||||
CacheMap<User, UserDto>(src => src.DefaultPermissions, dto => dto.DefaultPermissions);
|
||||
CacheMap<User, UserDto>(src => src.StartMediaId, dto => dto.MediaStartId);
|
||||
CacheMap<User, UserDto>(src => src.StartContentId, dto => dto.ContentStartId);
|
||||
CacheMap<User, UserDto>(src => src.DefaultToLiveEditing, dto => dto.DefaultToLiveEditing);
|
||||
@@ -38,22 +43,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<User, UserDto>(src => src.Language, dto => dto.UserLanguage);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(UserType))]
|
||||
public sealed class UserTypeMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
@@ -25,6 +25,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<UserType, UserTypeDto>(src => src.Id, dto => dto.Id);
|
||||
@@ -33,21 +38,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<UserType, UserTypeDto>(src => src.Permissions, dto => dto.DefaultPermissions);
|
||||
}
|
||||
|
||||
internal override string Map(string propertyName)
|
||||
{
|
||||
if (!PropertyInfoCache.ContainsKey(propertyName))
|
||||
return string.Empty;
|
||||
|
||||
var dtoTypeProperty = PropertyInfoCache[propertyName];
|
||||
|
||||
return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
|
||||
internal override void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var property = base.ResolveMapping(sourceMember, destinationMember);
|
||||
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -223,8 +223,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 4, DataTypeId = -88, ControlId = new Guid(Constants.PropertyEditors.Textbox), DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 5, DataTypeId = -89, ControlId = new Guid(Constants.PropertyEditors.TextboxMultiple), DbType = "Ntext" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 6, DataTypeId = -90, ControlId = new Guid(Constants.PropertyEditors.UploadField), DbType = "Nvarchar" });
|
||||
//Dropdown list
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -91, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 7, DataTypeId = -92, ControlId = new Guid(Constants.PropertyEditors.NoEdit), DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 8, DataTypeId = -36, ControlId = new Guid(Constants.PropertyEditors.DateTime), DbType = "Date" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, ControlId = new Guid(Constants.PropertyEditors.ColorPicker), DbType = "Nvarchar" });
|
||||
@@ -234,24 +232,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, ControlId = new Guid(Constants.PropertyEditors.Date), DbType = "Date" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 14, DataTypeId = -42, ControlId = new Guid(Constants.PropertyEditors.DropDownList), DbType = "Integer" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, ControlId = new Guid(Constants.PropertyEditors.CheckBoxList), DbType = "Nvarchar" });
|
||||
// Fix for rich text editor backwards compatibility -> 83722133-f80c-4273-bdb6-1befaa04a612 TinyMCE DataType
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 22, DataTypeId = -44, ControlId = new Guid("a3776494-0574-4d93-b7de-efdfdec6f2d1"), DbType = "Ntext" });
|
||||
//Radiobutton list
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 23, DataTypeId = -128, ControlId = new Guid("a52c7c1c-c330-476e-8605-d63d3b84b6a6"), DbType = "Nvarchar" });
|
||||
//Dropdown list multiple
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 24, DataTypeId = -129, ControlId = new Guid("928639ed-9c73-4028-920c-1e55dbb68783"), DbType = "Nvarchar" });
|
||||
//Dropdown list
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 25, DataTypeId = -130, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" });
|
||||
//Dropdown list
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 26, DataTypeId = -131, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" });
|
||||
//Dropdown list
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 27, DataTypeId = -132, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" });
|
||||
//No edit
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 28, DataTypeId = -133, ControlId = new Guid("6c738306-4c17-4d88-b9bd-6546f3771597"), DbType = "Ntext" });
|
||||
//Dropdown list multiple
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 29, DataTypeId = -134, ControlId = new Guid("928639ed-9c73-4028-920c-1e55dbb68783"), DbType = "Nvarchar" });
|
||||
//Not found - maybe a legacy thing?
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 30, DataTypeId = -50, ControlId = new Guid("aaf99bb2-dbbe-444d-a296-185076bf0484"), DbType = "Date" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 16, DataTypeId = 1034, ControlId = new Guid(Constants.PropertyEditors.ContentPicker), DbType = "Integer" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 17, DataTypeId = 1035, ControlId = new Guid(Constants.PropertyEditors.MediaPicker), DbType = "Integer" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 18, DataTypeId = 1036, ControlId = new Guid(Constants.PropertyEditors.MemberPicker), DbType = "Integer" });
|
||||
|
||||
@@ -384,7 +384,7 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
|
||||
// Add a parameter to a DB command
|
||||
void AddParam(IDbCommand cmd, object item, string ParameterPrefix)
|
||||
internal void AddParam(IDbCommand cmd, object item, string ParameterPrefix)
|
||||
{
|
||||
// Convert value to from poco type to db type
|
||||
if (Database.Mapper != null && item!=null)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Migrations.Initial;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
@@ -28,6 +31,149 @@ namespace Umbraco.Core.Persistence
|
||||
CreateTable(db, overwrite, tableType);
|
||||
}
|
||||
|
||||
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection)
|
||||
{
|
||||
//don't do anything if there are no records.
|
||||
if (collection.Any() == false)
|
||||
return;
|
||||
|
||||
using (var tr = db.GetTransaction())
|
||||
{
|
||||
db.BulkInsertRecords(collection, tr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection, Transaction tr)
|
||||
{
|
||||
//don't do anything if there are no records.
|
||||
if (collection.Any() == false)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
//if it is sql ce or it is a sql server version less than 2008, we need to do individual inserts.
|
||||
var sqlServerSyntax = SqlSyntaxContext.SqlSyntaxProvider as SqlServerSyntaxProvider;
|
||||
if ((sqlServerSyntax != null && (int)sqlServerSyntax.VersionName.Value < (int)SqlServerVersionName.V2008)
|
||||
|| SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
|
||||
{
|
||||
//SqlCe doesn't support bulk insert statements!
|
||||
|
||||
foreach (var poco in collection)
|
||||
{
|
||||
db.Insert(poco);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] sqlStatements;
|
||||
var cmds = db.GenerateBulkInsertCommand(collection, db.Connection, out sqlStatements);
|
||||
for (var i = 0; i < sqlStatements.Length; i++)
|
||||
{
|
||||
using (var cmd = cmds[i])
|
||||
{
|
||||
cmd.CommandText = sqlStatements[i];
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr.Complete();
|
||||
}
|
||||
catch
|
||||
{
|
||||
tr.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a bulk insert command
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="connection"></param>
|
||||
/// <param name="sql"></param>
|
||||
/// <returns>Sql commands with populated command parameters required to execute the sql statement</returns>
|
||||
/// <remarks>
|
||||
/// The limits for number of parameters are 2100 (in sql server, I think there's many more allowed in mysql). So
|
||||
/// we need to detect that many params and split somehow.
|
||||
/// For some reason the 2100 limit is not actually allowed even though the exception from sql server mentions 2100 as a max, perhaps it is 2099
|
||||
/// that is max. I've reduced it to 2000 anyways.
|
||||
/// </remarks>
|
||||
internal static IDbCommand[] GenerateBulkInsertCommand<T>(
|
||||
this Database db,
|
||||
IEnumerable<T> collection,
|
||||
IDbConnection connection,
|
||||
out string[] sql)
|
||||
{
|
||||
//A filter used below a few times to get all columns except result cols and not the primary key if it is auto-incremental
|
||||
Func<Database.PocoData, KeyValuePair<string, Database.PocoColumn>, bool> includeColumn = (data, column) =>
|
||||
{
|
||||
if (column.Value.ResultColumn) return false;
|
||||
if (data.TableInfo.AutoIncrement && column.Key == data.TableInfo.PrimaryKey) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
var pd = Database.PocoData.ForType(typeof(T));
|
||||
var tableName = db.EscapeTableName(pd.TableInfo.TableName);
|
||||
|
||||
//get all columns to include and format for sql
|
||||
var cols = string.Join(", ",
|
||||
pd.Columns
|
||||
.Where(c => includeColumn(pd, c))
|
||||
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
|
||||
|
||||
var itemArray = collection.ToArray();
|
||||
|
||||
//calculate number of parameters per item
|
||||
var paramsPerItem = pd.Columns.Count(i => includeColumn(pd, i));
|
||||
|
||||
//Example calc:
|
||||
// Given: we have 4168 items in the itemArray, each item contains 8 command parameters (values to be inserterted)
|
||||
// 2100 / 8 = 262.5
|
||||
// Math.Floor(2100 / 8) = 262 items per trans
|
||||
// 4168 / 262 = 15.908... = there will be 16 trans in total
|
||||
|
||||
//all items will be included if we have disabled db parameters
|
||||
var itemsPerTrans = Math.Floor(2000.00 / paramsPerItem);
|
||||
//there will only be one transaction if we have disabled db parameters
|
||||
var numTrans = Math.Ceiling(itemArray.Length / itemsPerTrans);
|
||||
|
||||
var sqlQueries = new List<string>();
|
||||
var commands = new List<IDbCommand>();
|
||||
|
||||
for (var tIndex = 0; tIndex < numTrans; tIndex++)
|
||||
{
|
||||
var itemsForTrans = itemArray
|
||||
.Skip(tIndex * (int)itemsPerTrans)
|
||||
.Take((int)itemsPerTrans);
|
||||
|
||||
var cmd = db.CreateCommand(connection, "");
|
||||
var pocoValues = new List<string>();
|
||||
var index = 0;
|
||||
foreach (var poco in itemsForTrans)
|
||||
{
|
||||
var values = new List<string>();
|
||||
//get all columns except result cols and not the primary key if it is auto-incremental
|
||||
foreach (var i in pd.Columns.Where(x => includeColumn(pd, x)))
|
||||
{
|
||||
db.AddParam(cmd, i.Value.GetValue(poco), "@");
|
||||
values.Add(string.Format("{0}{1}", "@", index++));
|
||||
}
|
||||
pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")");
|
||||
}
|
||||
|
||||
var sqlResult = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
|
||||
sqlQueries.Add(sqlResult);
|
||||
commands.Add(cmd);
|
||||
}
|
||||
|
||||
sql = sqlQueries.ToArray();
|
||||
|
||||
return commands.ToArray();
|
||||
}
|
||||
|
||||
public static void CreateTable(this Database db, bool overwrite, Type modelType)
|
||||
{
|
||||
var tableDefinition = DefinitionFactory.GetTableDefinition(modelType);
|
||||
@@ -67,7 +213,7 @@ namespace Umbraco.Core.Persistence
|
||||
//Turn on identity insert if db provider is not mysql
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity))
|
||||
db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName))));
|
||||
|
||||
|
||||
//Call the NewTable-event to trigger the insert of base/default data
|
||||
NewTable(tableName, db, e);
|
||||
|
||||
@@ -106,7 +252,7 @@ namespace Umbraco.Core.Persistence
|
||||
public static void DropTable<T>(this Database db)
|
||||
where T : new()
|
||||
{
|
||||
Type type = typeof (T);
|
||||
Type type = typeof(T);
|
||||
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
|
||||
if (tableNameAttribute == null)
|
||||
throw new Exception(
|
||||
@@ -184,5 +330,5 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
}
|
||||
|
||||
internal class TableCreationEventArgs : System.ComponentModel.CancelEventArgs{}
|
||||
internal class TableCreationEventArgs : System.ComponentModel.CancelEventArgs { }
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic that is shared with the expression helpers
|
||||
/// </summary>
|
||||
internal class BaseExpressionHelper
|
||||
{
|
||||
public virtual string GetQuotedValue(object value, Type fieldType, Func<object, string> escapeCallback = null, Func<Type, bool> shouldQuoteCallback = null)
|
||||
{
|
||||
if (value == null) return "NULL";
|
||||
|
||||
if (escapeCallback == null)
|
||||
{
|
||||
escapeCallback = EscapeParam;
|
||||
}
|
||||
if (shouldQuoteCallback == null)
|
||||
{
|
||||
shouldQuoteCallback = ShouldQuoteValue;
|
||||
}
|
||||
|
||||
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
|
||||
{
|
||||
//if (TypeSerializer.CanCreateFromString(fieldType))
|
||||
//{
|
||||
// return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'";
|
||||
//}
|
||||
|
||||
throw new NotSupportedException(
|
||||
string.Format("Property of type: {0} is not supported", fieldType.FullName));
|
||||
}
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
return ((int)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(float))
|
||||
return ((float)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(double))
|
||||
return ((double)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(decimal))
|
||||
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(DateTime))
|
||||
{
|
||||
return "'" + EscapeParam(((DateTime)value).ToIsoString()) + "'";
|
||||
}
|
||||
|
||||
if (fieldType == typeof(bool))
|
||||
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
|
||||
|
||||
return ShouldQuoteValue(fieldType)
|
||||
? "'" + EscapeParam(value) + "'"
|
||||
: value.ToString();
|
||||
}
|
||||
|
||||
public virtual string EscapeParam(object paramValue)
|
||||
{
|
||||
return paramValue.ToString().Replace("'", "''");
|
||||
}
|
||||
|
||||
public virtual string EscapeAtArgument(string exp)
|
||||
{
|
||||
if (exp.StartsWith("@"))
|
||||
return string.Concat("@", exp);
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
public virtual bool ShouldQuoteValue(Type fieldType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual string RemoveQuote(string exp)
|
||||
{
|
||||
if (exp.StartsWith("'") && exp.EndsWith("'"))
|
||||
{
|
||||
exp = exp.Remove(0, 1);
|
||||
exp = exp.Remove(exp.Length - 1, 1);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
protected virtual string RemoveQuoteFromAlias(string exp)
|
||||
{
|
||||
|
||||
if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
|
||||
&&
|
||||
(exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
|
||||
{
|
||||
exp = exp.Remove(0, 1);
|
||||
exp = exp.Remove(exp.Length - 1, 1);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
internal class ModelToSqlExpressionHelper<T>
|
||||
internal class ModelToSqlExpressionHelper<T> : BaseExpressionHelper
|
||||
{
|
||||
private string sep = " ";
|
||||
private BaseMapper _mapper;
|
||||
@@ -246,7 +246,7 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
case "ToLower":
|
||||
return string.Format("lower({0})", r);
|
||||
case "StartsWith":
|
||||
return string.Format("upper({0}) like '{1}%'", r, RemoveQuote(args[0].ToString().ToUpper()));
|
||||
return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper())));
|
||||
case "EndsWith":
|
||||
return string.Format("upper({0}) like '%{1}'", r, RemoveQuote(args[0].ToString()).ToUpper());
|
||||
case "Contains":
|
||||
@@ -435,69 +435,7 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
|
||||
public virtual string GetQuotedValue(object value, Type fieldType)
|
||||
{
|
||||
if (value == null) return "NULL";
|
||||
|
||||
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
string.Format("Property of type: {0} is not supported", fieldType.FullName));
|
||||
}
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
return ((int)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(float))
|
||||
return ((float)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(double))
|
||||
return ((double)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(decimal))
|
||||
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(DateTime))
|
||||
return "'" + EscapeParam(((DateTime)value).ToString(CultureInfo.InvariantCulture)) + "'";
|
||||
|
||||
if (fieldType == typeof(bool))
|
||||
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
|
||||
|
||||
return ShouldQuoteValue(fieldType)
|
||||
? "'" + EscapeParam(value) + "'"
|
||||
: value.ToString();
|
||||
}
|
||||
|
||||
public virtual string EscapeParam(object paramValue)
|
||||
{
|
||||
return paramValue.ToString().Replace("'", "''");
|
||||
}
|
||||
|
||||
public virtual bool ShouldQuoteValue(Type fieldType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected string RemoveQuote(string exp)
|
||||
{
|
||||
|
||||
if (exp.StartsWith("'") && exp.EndsWith("'"))
|
||||
{
|
||||
exp = exp.Remove(0, 1);
|
||||
exp = exp.Remove(exp.Length - 1, 1);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
protected string RemoveQuoteFromAlias(string exp)
|
||||
{
|
||||
|
||||
if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
|
||||
&&
|
||||
(exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
|
||||
{
|
||||
exp = exp.Remove(0, 1);
|
||||
exp = exp.Remove(exp.Length - 1, 1);
|
||||
}
|
||||
return exp;
|
||||
return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue);
|
||||
}
|
||||
|
||||
private string GetTrueExpression()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
@@ -9,7 +8,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
internal class PocoToSqlExpressionHelper<T>
|
||||
internal class PocoToSqlExpressionHelper<T> : BaseExpressionHelper
|
||||
{
|
||||
private string sep = " ";
|
||||
private Database.PocoData pd;
|
||||
@@ -251,7 +250,7 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
case "ToLower":
|
||||
return string.Format("lower({0})", r);
|
||||
case "StartsWith":
|
||||
return string.Format("upper({0}) like '{1}%'", r, RemoveQuote(args[0].ToString().ToUpper()));
|
||||
return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper())));
|
||||
case "EndsWith":
|
||||
return string.Format("upper({0}) like '%{1}'", r, RemoveQuote(args[0].ToString()).ToUpper());
|
||||
case "Contains":
|
||||
@@ -440,50 +439,7 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
|
||||
public virtual string GetQuotedValue(object value, Type fieldType)
|
||||
{
|
||||
if (value == null) return "NULL";
|
||||
|
||||
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
|
||||
{
|
||||
//if (TypeSerializer.CanCreateFromString(fieldType))
|
||||
//{
|
||||
// return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'";
|
||||
//}
|
||||
|
||||
throw new NotSupportedException(
|
||||
string.Format("Property of type: {0} is not supported", fieldType.FullName));
|
||||
}
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
return ((int)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(float))
|
||||
return ((float)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(double))
|
||||
return ((double)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(decimal))
|
||||
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if(fieldType == typeof(DateTime))
|
||||
return "'" + EscapeParam(((DateTime)value).ToString(CultureInfo.InvariantCulture)) + "'";
|
||||
|
||||
if (fieldType == typeof(bool))
|
||||
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
|
||||
|
||||
return ShouldQuoteValue(fieldType)
|
||||
? "'" + EscapeParam(value) + "'"
|
||||
: value.ToString();
|
||||
}
|
||||
|
||||
public virtual string EscapeParam(object paramValue)
|
||||
{
|
||||
return paramValue.ToString().Replace("'", "''");
|
||||
}
|
||||
|
||||
public virtual bool ShouldQuoteValue(Type fieldType)
|
||||
{
|
||||
return true;
|
||||
return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue);
|
||||
}
|
||||
|
||||
protected virtual string GetFieldName(Database.PocoData pocoData, string name)
|
||||
@@ -494,30 +450,6 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(column.Value.ColumnName));
|
||||
}
|
||||
|
||||
protected string RemoveQuote(string exp)
|
||||
{
|
||||
|
||||
if (exp.StartsWith("'") && exp.EndsWith("'"))
|
||||
{
|
||||
exp = exp.Remove(0, 1);
|
||||
exp = exp.Remove(exp.Length - 1, 1);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
protected string RemoveQuoteFromAlias(string exp)
|
||||
{
|
||||
|
||||
if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
|
||||
&&
|
||||
(exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
|
||||
{
|
||||
exp = exp.Remove(0, 1);
|
||||
exp = exp.Remove(exp.Length - 1, 1);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
private string GetTrueExpression()
|
||||
{
|
||||
object o = GetQuotedTrueValue();
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Relators
|
||||
{
|
||||
internal class UserSectionRelator
|
||||
{
|
||||
internal UserDto Current;
|
||||
|
||||
internal UserDto Map(UserDto a, User2AppDto p)
|
||||
{
|
||||
// Terminating call. Since we can return null from this function
|
||||
// we need to be ready for PetaPoco to callback later with null
|
||||
// parameters
|
||||
if (a == null)
|
||||
return Current;
|
||||
|
||||
// Is this the same DictionaryItem as the current one we're processing
|
||||
if (Current != null && Current.Id == a.Id)
|
||||
{
|
||||
// Yes, just add this User2AppDto to the current item's collection
|
||||
Current.User2AppDtos.Add(p);
|
||||
|
||||
// Return null to indicate we're not done with this User yet
|
||||
return null;
|
||||
}
|
||||
|
||||
// This is a different User to the current one, or this is the
|
||||
// first time through and we don't have one yet
|
||||
|
||||
// Save the current User
|
||||
var prev = Current;
|
||||
|
||||
// Setup the new current User
|
||||
Current = a;
|
||||
Current.User2AppDtos = new List<User2AppDto>();
|
||||
//this can be null since we are doing a left join
|
||||
if (p.AppAlias != null)
|
||||
{
|
||||
Current.User2AppDtos.Add(p);
|
||||
}
|
||||
|
||||
// Return the now populated previous User (or null if first time through)
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,11 +170,31 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return content;
|
||||
}
|
||||
|
||||
public override void DeleteVersion(Guid versionId)
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<DocumentDto>()
|
||||
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
|
||||
.Where<ContentVersionDto>(x => x.VersionId == versionId)
|
||||
.Where<DocumentDto>(x => x.Newest == true);
|
||||
var dto = Database.Fetch<DocumentDto, ContentVersionDto>(sql).FirstOrDefault();
|
||||
|
||||
if(dto == null) return;
|
||||
|
||||
using (var transaction = Database.GetTransaction())
|
||||
{
|
||||
PerformDeleteVersion(dto.NodeId, versionId);
|
||||
|
||||
transaction.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PerformDeleteVersion(int id, Guid versionId)
|
||||
{
|
||||
Database.Delete<PreviewXmlDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<PropertyDataDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<ContentVersionDto>("WHERE nodeId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<PropertyDataDto>("WHERE contentNodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<ContentVersionDto>("WHERE ContentId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<DocumentDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
}
|
||||
|
||||
@@ -206,7 +226,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
|
||||
nodeDto.SortOrder = sortOrder;
|
||||
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
|
||||
|
||||
|
||||
//Update with new correct path
|
||||
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
|
||||
Database.Update(nodeDto);
|
||||
@@ -217,6 +237,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
entity.SortOrder = sortOrder;
|
||||
entity.Level = level;
|
||||
|
||||
|
||||
//Assign the same permissions to it as the parent node
|
||||
// http://issues.umbraco.org/issue/U4-2161
|
||||
var parentPermissions = GetPermissionsForEntity(entity.ParentId).ToArray();
|
||||
//if there are parent permissions then assign them, otherwise leave null and permissions will become the
|
||||
// user's default permissions.
|
||||
if (parentPermissions.Any())
|
||||
{
|
||||
var userPermissions = parentPermissions.Select(
|
||||
permissionDto => new KeyValuePair<object, string>(
|
||||
permissionDto.UserId,
|
||||
permissionDto.Permission));
|
||||
AssignEntityPermissions(entity, userPermissions);
|
||||
//flag the entity's permissions changed flag so we can track those changes.
|
||||
//Currently only used for the cache refreshers to detect if we should refresh all user permissions cache.
|
||||
((Content) entity).PermissionsChanged = true;
|
||||
}
|
||||
|
||||
//Create the Content specific data - cmsContent
|
||||
var contentDto = dto.ContentVersionDto.ContentDto;
|
||||
contentDto.NodeId = nodeDto.NodeId;
|
||||
@@ -257,8 +295,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
protected override void PersistUpdatedItem(IContent entity)
|
||||
{
|
||||
var publishedState = ((Content) entity).PublishedState;
|
||||
//A new version should only be created if published state (or language) has changed
|
||||
bool shouldCreateNewVersion = (((ICanBeDirty)entity).IsPropertyDirty("Published") && publishedState != PublishedState.Unpublished) || ((ICanBeDirty)entity).IsPropertyDirty("Language");
|
||||
|
||||
//check if we need to create a new version
|
||||
bool shouldCreateNewVersion = entity.ShouldCreateNewVersion(publishedState);
|
||||
if (shouldCreateNewVersion)
|
||||
{
|
||||
//Updates Modified date and Version Guid
|
||||
@@ -283,6 +322,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
|
||||
new {ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId});
|
||||
entity.SortOrder = maxSortOrder + 1;
|
||||
|
||||
//Question: If we move a node, should we update permissions to inherit from the new parent if the parent has permissions assigned?
|
||||
// if we do that, then we'd need to propogate permissions all the way downward which might not be ideal for many people.
|
||||
// Gonna just leave it as is for now, and not re-propogate permissions.
|
||||
}
|
||||
|
||||
var factory = new ContentFactory(NodeObjectTypeId, entity.Id);
|
||||
@@ -305,7 +348,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//If Published state has changed then previous versions should have their publish state reset.
|
||||
//If state has been changed to unpublished the previous versions publish state should also be reset.
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished))
|
||||
//if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished))
|
||||
if (entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, shouldCreateNewVersion))
|
||||
{
|
||||
var publishedDocs = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND published = @IsPublished", new { Id = entity.Id, IsPublished = true });
|
||||
foreach (var doc in publishedDocs)
|
||||
@@ -316,19 +360,18 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
//Look up (newest) entries by id in cmsDocument table to set newest = false
|
||||
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND newest = @IsNewest", new { Id = entity.Id, IsNewest = true });
|
||||
foreach (var documentDto in documentDtos)
|
||||
{
|
||||
var docDto = documentDto;
|
||||
docDto.Newest = false;
|
||||
Database.Update(docDto);
|
||||
}
|
||||
|
||||
var contentVersionDto = dto.ContentVersionDto;
|
||||
if (shouldCreateNewVersion)
|
||||
{
|
||||
//Look up (newest) entries by id in cmsDocument table to set newest = false
|
||||
//NOTE: This is only relevant when a new version is created, which is why its done inside this if-statement.
|
||||
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND newest = @IsNewest", new { Id = entity.Id, IsNewest = true });
|
||||
foreach (var documentDto in documentDtos)
|
||||
{
|
||||
var docDto = documentDto;
|
||||
docDto.Newest = false;
|
||||
Database.Update(docDto);
|
||||
}
|
||||
|
||||
//Create a new version - cmsContentVersion
|
||||
//Assumes a new Version guid and Version date (modified date) has been set
|
||||
Database.Insert(contentVersionDto);
|
||||
@@ -427,7 +470,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
yield return CreateContentFromDto(dto, dto.VersionId);
|
||||
//Check in the cache first. If it exists there AND it is published
|
||||
// then we can use that entity. Otherwise if it is not published (which can be the case
|
||||
// because we only store the 'latest' entries in the cache which might not be the published
|
||||
// version)
|
||||
var fromCache = TryGetFromCache(dto.NodeId);
|
||||
if (fromCache.Success && fromCache.Result.Published)
|
||||
{
|
||||
yield return fromCache.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return CreateContentFromDto(dto, dto.VersionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +502,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
|
||||
/// </summary>
|
||||
@@ -510,7 +565,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.ParentId == parentId && x.Text.StartsWith(nodeName));
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName));
|
||||
|
||||
int uniqueNumber = 1;
|
||||
var currentName = nodeName;
|
||||
@@ -533,45 +588,5 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return currentName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Comparer that takes into account the duplicate index of a node name
|
||||
/// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc.
|
||||
/// </summary>
|
||||
private class SimilarNodeNameComparer : IComparer<string>
|
||||
{
|
||||
public int Compare(string x, string y)
|
||||
{
|
||||
if (x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1)
|
||||
{
|
||||
if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('(')))
|
||||
{
|
||||
int xDuplicateIndex = ExtractDuplicateIndex(x);
|
||||
int yDuplicateIndex = ExtractDuplicateIndex(y);
|
||||
|
||||
if (xDuplicateIndex != 0 && yDuplicateIndex != 0)
|
||||
{
|
||||
return xDuplicateIndex.CompareTo(yDuplicateIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.Compare(x.ToLower(), y.ToLower(), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private int ExtractDuplicateIndex(string text)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2)
|
||||
{
|
||||
int startPos = text.LastIndexOf('(') + 1;
|
||||
int length = text.Length - 1 - startPos;
|
||||
|
||||
int.TryParse(text.Substring(startPos, length), out index);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
|
||||
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
|
||||
var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId).Append(GetGroupBy(isContent, isMedia));
|
||||
var sql = GetBaseWhere(GetBase, isContent, isMedia, string.Empty, objectTypeId).Append(GetGroupBy(isContent, isMedia));
|
||||
var dtos = isMedia
|
||||
? _work.Database.Fetch<UmbracoEntityDto, UmbracoPropertyDto, UmbracoEntityDto>(
|
||||
new UmbracoEntityRelator().Map, sql)
|
||||
@@ -101,7 +101,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public virtual IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query)
|
||||
{
|
||||
var sqlClause = GetBase(false, false);
|
||||
var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query<IUmbracoEntity>) query).WhereClauses()));
|
||||
var sqlClause = GetBase(false, false, wheres);
|
||||
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
|
||||
var sql = translator.Translate().Append(GetGroupBy(false, false));
|
||||
|
||||
@@ -117,7 +118,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
|
||||
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
|
||||
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId);
|
||||
|
||||
var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query<IUmbracoEntity>)query).WhereClauses()));
|
||||
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, wheres, objectTypeId);
|
||||
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
|
||||
var sql = translator.Translate().Append(GetGroupBy(isContent, isMedia));
|
||||
|
||||
@@ -136,7 +139,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Sql Statements
|
||||
|
||||
protected virtual Sql GetBase(bool isContent, bool isMedia)
|
||||
protected virtual Sql GetBase(bool isContent, bool isMedia, string additionWhereStatement = "")
|
||||
{
|
||||
var columns = new List<object>
|
||||
{
|
||||
@@ -187,42 +190,38 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.On("umbracoNode.id = latest.nodeId");
|
||||
}
|
||||
|
||||
/*if (isContent)
|
||||
{
|
||||
sql.LeftJoin(
|
||||
"(SELECT contentNodeId, versionId, dataNvarchar, controlId FROM cmsPropertyData INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid" +
|
||||
" INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId) as property")
|
||||
.On("umbracoNode.id = property.contentNodeId AND latest.versionId = property.versionId");
|
||||
}*/
|
||||
if (isMedia)
|
||||
{
|
||||
sql.LeftJoin(
|
||||
"(SELECT contentNodeId, versionId, dataNvarchar, controlId FROM cmsPropertyData INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid" +
|
||||
" INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId) as property")
|
||||
"(SELECT contentNodeId, versionId, dataNvarchar, controlId FROM cmsPropertyData " +
|
||||
"INNER JOIN umbracoNode ON cmsPropertyData.contentNodeId = umbracoNode.id " +
|
||||
"INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid " +
|
||||
"INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId "+
|
||||
"WHERE umbracoNode.nodeObjectType = '" + Constants.ObjectTypes.Media + "'" + additionWhereStatement + ") as property")
|
||||
.On("umbracoNode.id = property.contentNodeId");
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, Sql> baseQuery, bool isContent, bool isMedia, Guid id)
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, string additionWhereStatement, Guid id)
|
||||
{
|
||||
var sql = baseQuery(isContent, isMedia)
|
||||
var sql = baseQuery(isContent, isMedia, additionWhereStatement)
|
||||
.Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = id });
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, Sql> baseQuery, bool isContent, bool isMedia, int id)
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, int id)
|
||||
{
|
||||
var sql = baseQuery(isContent, isMedia)
|
||||
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'")
|
||||
.Where("umbracoNode.id = @Id", new { Id = id })
|
||||
.Append(GetGroupBy(isContent, isMedia));
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, Sql> baseQuery, bool isContent, bool isMedia, Guid objectId, int id)
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, Guid objectId, int id)
|
||||
{
|
||||
var sql = baseQuery(isContent, isMedia)
|
||||
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'")
|
||||
.Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType",
|
||||
new {Id = id, NodeObjectType = objectId});
|
||||
return sql;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
IContent GetByLanguage(int id, string language);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all published Content byh the specified query
|
||||
/// Gets all published Content by the specified query
|
||||
/// </summary>
|
||||
/// <param name="query">Query to execute against published versions</param>
|
||||
/// <returns>An enumerable list of <see cref="IContent"/></returns>
|
||||
|
||||
@@ -4,6 +4,5 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMediaRepository : IRepositoryVersionable<int, IMedia>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">Type of <see cref="IAggregateRoot"/> entity for which the repository is used</typeparam>
|
||||
/// <typeparam name="TId">Type of the Id used for this entity</typeparam>
|
||||
public interface IRepositoryQueryable<TId, TEntity> : IRepository<TId, TEntity>
|
||||
where TEntity : IAggregateRoot
|
||||
public interface IRepositoryQueryable<in TId, TEntity> : IRepository<TId, TEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified type and query
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal interface IUserRepository : IRepositoryQueryable<int, IUser>
|
||||
{
|
||||
IProfile GetProfileById(int id);
|
||||
//IProfile GetProfileById(int id);
|
||||
//IProfile GetProfileByUserName(string username);
|
||||
//IUser GetUserByUserName(string username);
|
||||
|
||||
/// <summary>
|
||||
/// This is useful when an entire section is removed from config
|
||||
/// </summary>
|
||||
/// <param name="sectionAlias"></param>
|
||||
IEnumerable<IUser> GetUsersAssignedToSection(string sectionAlias);
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,20 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
: base(work)
|
||||
{
|
||||
_mediaTypeRepository = mediaTypeRepository;
|
||||
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
|
||||
public MediaRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMediaTypeRepository mediaTypeRepository)
|
||||
: base(work, cache)
|
||||
{
|
||||
_mediaTypeRepository = mediaTypeRepository;
|
||||
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
|
||||
public bool EnsureUniqueNaming { get; set; }
|
||||
|
||||
#region Overrides of RepositoryBase<int,IMedia>
|
||||
|
||||
protected override IMedia PerformGet(int id)
|
||||
@@ -170,8 +176,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
protected override void PerformDeleteVersion(int id, Guid versionId)
|
||||
{
|
||||
Database.Delete<PreviewXmlDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<PropertyDataDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<ContentVersionDto>("WHERE nodeId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<PropertyDataDto>("WHERE contentNodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
Database.Delete<ContentVersionDto>("WHERE ContentId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -182,6 +188,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
((Models.Media)entity).AddingEntity();
|
||||
|
||||
//Ensure unique name on the same level
|
||||
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
|
||||
|
||||
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
|
||||
var dto = factory.BuildDto(entity);
|
||||
|
||||
@@ -246,6 +255,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Updates Modified date
|
||||
((Models.Media)entity).UpdatingEntity();
|
||||
|
||||
//Ensure unique name on the same level
|
||||
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name, entity.Id);
|
||||
|
||||
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("ParentId"))
|
||||
{
|
||||
@@ -370,5 +382,37 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return new PropertyCollection(properties);
|
||||
}
|
||||
|
||||
private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0)
|
||||
{
|
||||
if (EnsureUniqueNaming == false)
|
||||
return nodeName;
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName));
|
||||
|
||||
int uniqueNumber = 1;
|
||||
var currentName = nodeName;
|
||||
|
||||
var dtos = Database.Fetch<NodeDto>(sql);
|
||||
if (dtos.Any())
|
||||
{
|
||||
var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer());
|
||||
foreach (var dto in results)
|
||||
{
|
||||
if (id != 0 && id == dto.NodeId) continue;
|
||||
|
||||
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
|
||||
{
|
||||
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
|
||||
uniqueNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// A repository that exposes functionality to modify assigned permissions to a node
|
||||
/// </summary>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
internal abstract class PermissionRepository<TId, TEntity> : PetaPocoRepositoryBase<TId, TEntity>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
protected PermissionRepository(IDatabaseUnitOfWork work)
|
||||
: base(work)
|
||||
{
|
||||
}
|
||||
|
||||
protected PermissionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
|
||||
: base(work, cache)
|
||||
{
|
||||
}
|
||||
|
||||
protected internal IEnumerable<User2NodePermissionDto> GetPermissionsForEntity(int entityId)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<User2NodePermissionDto>()
|
||||
.Where<User2NodePermissionDto>(dto => dto.NodeId == entityId);
|
||||
return Database.Fetch<User2NodePermissionDto>(sql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns permissions to an entity for multiple users
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="permissions"></param>
|
||||
/// <param name="userIds"></param>
|
||||
protected internal void AssignEntityPermissions(TEntity entity, string permissions, IEnumerable<object> userIds)
|
||||
{
|
||||
var actions = userIds.Select(id => new User2NodePermissionDto
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
Permission = permissions,
|
||||
UserId = (int)id
|
||||
});
|
||||
|
||||
Database.BulkInsertRecords(actions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns permissions to an entity for multiple users/permission entries
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="userPermissions">
|
||||
/// A key/value pair list containing a userId and a permission to assign
|
||||
/// </param>
|
||||
protected internal void AssignEntityPermissions(TEntity entity, IEnumerable<KeyValuePair<object, string>> userPermissions)
|
||||
{
|
||||
var actions = userPermissions.Select(p => new User2NodePermissionDto
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
Permission = p.Value,
|
||||
UserId = (int)p.Key
|
||||
});
|
||||
|
||||
Database.BulkInsertRecords(actions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace permissions for an entity for multiple users
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="permissions"></param>
|
||||
/// <param name="userIds"></param>
|
||||
protected internal void ReplaceEntityPermissions(TEntity entity, string permissions, IEnumerable<object> userIds)
|
||||
{
|
||||
Database.Update<User2NodePermissionDto>(
|
||||
GenerateReplaceEntityPermissionsSql(entity.Id, permissions, userIds.ToArray()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An overload to replace entity permissions and all replace all descendant permissions
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="permissions"></param>
|
||||
/// <param name="getDescendantIds">
|
||||
/// A callback to get the descendant Ids of the current entity
|
||||
/// </param>
|
||||
/// <param name="userIds"></param>
|
||||
protected internal void ReplaceEntityPermissions(TEntity entity, string permissions, Func<IEntity, IEnumerable<int>> getDescendantIds, IEnumerable<object> userIds)
|
||||
{
|
||||
Database.Update<User2NodePermissionDto>(
|
||||
GenerateReplaceEntityPermissionsSql(
|
||||
new[] {entity.Id}.Concat(getDescendantIds(entity)).ToArray(),
|
||||
permissions,
|
||||
userIds.ToArray()));
|
||||
}
|
||||
|
||||
internal static string GenerateReplaceEntityPermissionsSql(int entityId, string permissions, object[] userIds)
|
||||
{
|
||||
return GenerateReplaceEntityPermissionsSql(new[] {entityId}, permissions, userIds);
|
||||
}
|
||||
|
||||
internal static string GenerateReplaceEntityPermissionsSql(int[] entityIds, string permissions, object[] userIds)
|
||||
{
|
||||
//create the "SET" clause of the update statement
|
||||
var sqlSet = string.Format("SET {0}={1}",
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("permission"),
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedValue(permissions));
|
||||
|
||||
//build the nodeIds part of the where clause
|
||||
var sqlNodeWhere = BuildOrClause(entityIds, "nodeId");
|
||||
|
||||
//build up the userIds part of the where clause
|
||||
var userWhereBuilder = BuildOrClause(userIds, "userId");
|
||||
|
||||
var sqlWhere = new Sql();
|
||||
sqlWhere.Where(string.Format("{0} AND {1}", sqlNodeWhere, userWhereBuilder));
|
||||
|
||||
return string.Format("{0} {1}", sqlSet, sqlWhere.SQL);
|
||||
}
|
||||
|
||||
private static string BuildOrClause<T>(IEnumerable<T> ids, string colName)
|
||||
{
|
||||
var asArray = ids.ToArray();
|
||||
var userWhereBuilder = new StringBuilder();
|
||||
userWhereBuilder.Append("(");
|
||||
for (var index = 0; index < asArray.Length; index++)
|
||||
{
|
||||
var userId = asArray[index];
|
||||
userWhereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(colName));
|
||||
userWhereBuilder.Append("=");
|
||||
userWhereBuilder.Append(userId);
|
||||
if (index < asArray.Length - 1)
|
||||
{
|
||||
userWhereBuilder.Append(" OR ");
|
||||
}
|
||||
}
|
||||
userWhereBuilder.Append(")");
|
||||
return userWhereBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a repository specific to the Recycle Bins
|
||||
/// available for Content and Media.
|
||||
/// </summary>
|
||||
internal class RecycleBinRepository : DisposableObject
|
||||
{
|
||||
private readonly IDatabaseUnitOfWork _unitOfWork;
|
||||
|
||||
public RecycleBinRepository(IDatabaseUnitOfWork unitOfWork)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of files, which are referenced on items in the Recycle Bin.
|
||||
/// The list is generated by the convention that a file is referenced by
|
||||
/// the Upload data type or a property type with the alias 'umbracoFile'.
|
||||
/// </summary>
|
||||
/// <param name="nodeObjectType"></param>
|
||||
/// <returns></returns>
|
||||
public List<string> GetFilesInRecycleBin(Guid nodeObjectType)
|
||||
{
|
||||
var db = _unitOfWork.Database;
|
||||
|
||||
//Issue query to get all trashed content or media that has the Upload field as a property
|
||||
//The value for each field is stored in a list: FilesToDelete<string>()
|
||||
//Alias: Constants.Conventions.Media.File and ControlId: Constants.PropertyEditors.UploadField
|
||||
var sql = new Sql();
|
||||
sql.Select("DISTINCT(dataNvarchar)")
|
||||
.From<PropertyDataDto>()
|
||||
.InnerJoin<NodeDto>().On<PropertyDataDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<PropertyTypeDto>().On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
|
||||
.InnerJoin<DataTypeDto>().On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.DataTypeId)
|
||||
.Where("umbracoNode.trashed = '1' AND umbracoNode.nodeObjectType = @NodeObjectType AND dataNvarchar IS NOT NULL AND (cmsPropertyType.Alias = @FileAlias OR cmsDataType.controlId = @ControlId)",
|
||||
new { FileAlias = Constants.Conventions.Media.File, NodeObjectType = nodeObjectType, ControlId = Constants.PropertyEditors.UploadField });
|
||||
|
||||
var files = db.Fetch<string>(sql);
|
||||
return files;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of Ids for each of the items in the Recycle Bin.
|
||||
/// </summary>
|
||||
/// <param name="nodeObjectType"></param>
|
||||
/// <returns></returns>
|
||||
public List<int> GetIdsOfItemsInRecycleBin(Guid nodeObjectType)
|
||||
{
|
||||
var db = _unitOfWork.Database;
|
||||
|
||||
var idsSql = new Sql();
|
||||
idsSql.Select("DISTINCT(id)")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.Trashed && x.NodeObjectType == nodeObjectType);
|
||||
|
||||
var ids = db.Fetch<int>(idsSql);
|
||||
return ids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by running single bulk-Delete queries
|
||||
/// against the Content- or Media's Recycle Bin.
|
||||
/// </summary>
|
||||
/// <param name="nodeObjectType"></param>
|
||||
/// <returns></returns>
|
||||
public bool EmptyRecycleBin(Guid nodeObjectType)
|
||||
{
|
||||
var db = _unitOfWork.Database;
|
||||
|
||||
//Construct and execute delete statements for all trashed items by 'nodeObjectType'
|
||||
var deletes = new List<string>
|
||||
{
|
||||
FormatDeleteStatement("umbracoUser2NodeNotify", "nodeId"),
|
||||
FormatDeleteStatement("umbracoUser2NodePermission", "nodeId"),
|
||||
FormatDeleteStatement("umbracoRelation", "parentId"),
|
||||
FormatDeleteStatement("umbracoRelation", "childId"),
|
||||
FormatDeleteStatement("cmsTagRelationship", "nodeId"),
|
||||
FormatDeleteStatement("umbracoDomains", "domainRootStructureID"),
|
||||
FormatDeleteStatement("cmsDocument", "NodeId"),
|
||||
FormatDeleteStatement("cmsPropertyData", "contentNodeId"),
|
||||
FormatDeleteStatement("cmsPreviewXml", "nodeId"),
|
||||
FormatDeleteStatement("cmsContentVersion", "ContentId"),
|
||||
FormatDeleteStatement("cmsContentXml", "nodeID"),
|
||||
FormatDeleteStatement("cmsContent", "NodeId"),
|
||||
"UPDATE umbracoNode SET parentID = '-20' WHERE trashed = '1' AND nodeObjectType = @NodeObjectType",
|
||||
"DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType"
|
||||
};
|
||||
|
||||
//Wraps in transaction - this improves performance and also ensures
|
||||
// that if any of the deletions fails that the whole thing is rolled back.
|
||||
using (var trans = db.GetTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var delete in deletes)
|
||||
{
|
||||
db.Execute(delete, new { NodeObjectType = nodeObjectType });
|
||||
}
|
||||
|
||||
trans.Complete();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
trans.Dispose();
|
||||
LogHelper.Error<RecycleBinRepository>("An error occurred while emptying the Recycle Bin: " + ex.Message, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all files passed in.
|
||||
/// </summary>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
public bool DeleteFiles(IEnumerable<string> files)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
Parallel.ForEach(files, file =>
|
||||
{
|
||||
if (UmbracoSettings.UploadAllowDirectories)
|
||||
{
|
||||
var relativeFilePath = fs.GetRelativePath(file);
|
||||
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
|
||||
fs.DeleteDirectory(parentDirectory, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.DeleteFile(file, true);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<RecycleBinRepository>("An error occurred while deleting files attached to deleted nodes: " + ex.Message, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private string FormatDeleteStatement(string tableName, string keyName)
|
||||
{
|
||||
//This query works with sql ce and sql server:
|
||||
//DELETE FROM umbracoUser2NodeNotify WHERE umbracoUser2NodeNotify.nodeId IN
|
||||
//(SELECT nodeId FROM umbracoUser2NodeNotify as TB1 INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972')
|
||||
return
|
||||
string.Format(
|
||||
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT TB1.{1} FROM {0} as TB1 INNER JOIN umbracoNode as TB2 ON TB1.{1} = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
|
||||
tableName, keyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ensure the unit of work is disposed
|
||||
/// </remarks>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_unitOfWork.DisposeIfDisposable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,11 +85,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public TEntity Get(TId id)
|
||||
{
|
||||
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
|
||||
var rEntity = _cache.GetById(typeof(TEntity), key);
|
||||
if (rEntity != null)
|
||||
var fromCache = TryGetFromCache(id);
|
||||
if (fromCache.Success)
|
||||
{
|
||||
return (TEntity)rEntity;
|
||||
return fromCache.Result;
|
||||
}
|
||||
|
||||
var entity = PerformGet(id);
|
||||
@@ -102,7 +101,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
Entity asEntity = entity as Entity;
|
||||
TracksChangesEntityBase asEntity = entity as TracksChangesEntityBase;
|
||||
if (asEntity != null)
|
||||
{
|
||||
asEntity.ResetDirtyProperties(false);
|
||||
@@ -112,6 +111,17 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return entity;
|
||||
}
|
||||
|
||||
protected Attempt<TEntity> TryGetFromCache(TId id)
|
||||
{
|
||||
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
|
||||
var rEntity = _cache.GetById(typeof(TEntity), key);
|
||||
if (rEntity != null)
|
||||
{
|
||||
return new Attempt<TEntity>(true, (TEntity) rEntity);
|
||||
}
|
||||
return Attempt<TEntity>.False;
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetAll(params TId[] ids);
|
||||
/// <summary>
|
||||
/// Gets all entities of type TEntity or a list according to the passed in Ids
|
||||
@@ -173,14 +183,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public bool Exists(TId id)
|
||||
{
|
||||
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
|
||||
var rEntity = _cache.GetById(typeof(TEntity), key);
|
||||
if (rEntity != null)
|
||||
var fromCache = TryGetFromCache(id);
|
||||
if (fromCache.Success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return PerformExists(id);
|
||||
return PerformExists(id);
|
||||
}
|
||||
|
||||
protected abstract int PerformCount(IQuery<TEntity> query);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Comparer that takes into account the duplicate index of a node name
|
||||
/// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc.
|
||||
/// </summary>
|
||||
internal class SimilarNodeNameComparer : IComparer<string>
|
||||
{
|
||||
public int Compare(string x, string y)
|
||||
{
|
||||
if (x.LastIndexOf('(') != -1 && x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1)
|
||||
{
|
||||
if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('(')))
|
||||
{
|
||||
int xDuplicateIndex = ExtractDuplicateIndex(x);
|
||||
int yDuplicateIndex = ExtractDuplicateIndex(y);
|
||||
|
||||
if (xDuplicateIndex != 0 && yDuplicateIndex != 0)
|
||||
{
|
||||
return xDuplicateIndex.CompareTo(yDuplicateIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.Compare(x.ToLower(), y.ToLower(), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private int ExtractDuplicateIndex(string text)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2)
|
||||
{
|
||||
int startPos = text.LastIndexOf('(') + 1;
|
||||
int length = text.Length - 1 - startPos;
|
||||
|
||||
int.TryParse(text.Substring(startPos, length), out index);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Relators;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
@@ -13,17 +17,17 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <summary>
|
||||
/// Represents the UserRepository for doing CRUD operations for <see cref="IUser"/>
|
||||
/// </summary>
|
||||
internal class UserRepository : PetaPocoRepositoryBase<int, IUser>, IUserRepository
|
||||
internal class UserRepository : PermissionRepository<int, IUser>, IUserRepository
|
||||
{
|
||||
private readonly IUserTypeRepository _userTypeRepository;
|
||||
|
||||
public UserRepository(IDatabaseUnitOfWork work, IUserTypeRepository userTypeRepository)
|
||||
public UserRepository(IDatabaseUnitOfWork work, IUserTypeRepository userTypeRepository)
|
||||
: base(work)
|
||||
{
|
||||
_userTypeRepository = userTypeRepository;
|
||||
}
|
||||
|
||||
public UserRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository)
|
||||
public UserRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository)
|
||||
: base(work, cache)
|
||||
{
|
||||
_userTypeRepository = userTypeRepository;
|
||||
@@ -36,7 +40,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var dto = Database.FirstOrDefault<UserDto>(sql);
|
||||
var dto = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
@@ -52,18 +56,20 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
if (ids.Any())
|
||||
{
|
||||
foreach (var id in ids)
|
||||
{
|
||||
yield return Get(id);
|
||||
}
|
||||
return PerformGetAllOnIds(ids);
|
||||
}
|
||||
else
|
||||
|
||||
var sql = GetBaseQuery(false);
|
||||
|
||||
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql));
|
||||
}
|
||||
|
||||
private IEnumerable<IUser> PerformGetAllOnIds(params int[] ids)
|
||||
{
|
||||
if (ids.Any() == false) yield break;
|
||||
foreach (var id in ids)
|
||||
{
|
||||
var userDtos = Database.Fetch<UserDto>("WHERE id >= 0");
|
||||
foreach (var userDto in userDtos)
|
||||
{
|
||||
yield return Get(userDto.Id);
|
||||
}
|
||||
yield return Get(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +79,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var translator = new SqlTranslator<IUser>(sqlClause, query);
|
||||
var sql = translator.Translate();
|
||||
|
||||
var dtos = Database.Fetch<UserDto>(sql);
|
||||
var dtos = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql);
|
||||
|
||||
foreach (var dto in dtos.DistinctBy(x => x.Id))
|
||||
{
|
||||
@@ -88,20 +94,41 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select(isCount ? "COUNT(*)" : "*")
|
||||
.From<UserDto>();
|
||||
if (isCount)
|
||||
{
|
||||
sql.Select("COUNT(*)").From<UserDto>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetBaseQuery("*");
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
private static Sql GetBaseQuery(string columns)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select(columns)
|
||||
.From<UserDto>()
|
||||
.LeftJoin<User2AppDto>()
|
||||
.On<UserDto, User2AppDto>(left => left.Id, right => right.UserId);
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
return "umbracoUser.id = @Id";
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
{
|
||||
var list = new List<string>
|
||||
{
|
||||
"DELETE FROM umbracoUser2NodePermission WHERE userId = @Id",
|
||||
"DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id",
|
||||
"DELETE FROM umbracoUserLogins WHERE userId = @Id",
|
||||
"DELETE FROM umbracoUser2app WHERE " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@Id",
|
||||
"DELETE FROM umbracoUser WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
@@ -120,6 +147,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var id = Convert.ToInt32(Database.Insert(userDto));
|
||||
entity.Id = id;
|
||||
|
||||
foreach (var sectionDto in userDto.User2AppDtos)
|
||||
{
|
||||
//need to set the id explicitly here
|
||||
sectionDto.UserId = id;
|
||||
Database.Insert(sectionDto);
|
||||
}
|
||||
|
||||
((ICanBeDirty)entity).ResetDirtyProperties();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IUser entity)
|
||||
@@ -128,25 +163,89 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var userDto = userFactory.BuildDto(entity);
|
||||
|
||||
Database.Update(userDto);
|
||||
|
||||
//update the sections if they've changed
|
||||
var user = (User)entity;
|
||||
if (user.IsPropertyDirty("AllowedSections"))
|
||||
{
|
||||
//for any that exist on the object, we need to determine if we need to update or insert
|
||||
foreach (var sectionDto in userDto.User2AppDtos)
|
||||
{
|
||||
if (user.AddedSections.Contains(sectionDto.AppAlias))
|
||||
{
|
||||
//we need to insert since this was added
|
||||
Database.Insert(sectionDto);
|
||||
}
|
||||
else
|
||||
{
|
||||
//we need to manually update this record because it has a composite key
|
||||
Database.Update<User2AppDto>("SET app=@Section WHERE app=@Section AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@UserId",
|
||||
new { Section = sectionDto.AppAlias, UserId = sectionDto.UserId });
|
||||
}
|
||||
}
|
||||
|
||||
//now we need to delete any applications that have been removed
|
||||
foreach (var section in user.RemovedSections)
|
||||
{
|
||||
//we need to manually delete thsi record because it has a composite key
|
||||
Database.Delete<User2AppDto>("WHERE app=@Section AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@UserId",
|
||||
new { Section = section, UserId = (int)user.Id });
|
||||
}
|
||||
}
|
||||
|
||||
((ICanBeDirty)entity).ResetDirtyProperties();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IUserRepository
|
||||
|
||||
public IProfile GetProfileById(int id)
|
||||
|
||||
public IEnumerable<IUser> GetUsersAssignedToSection(string sectionAlias)
|
||||
{
|
||||
//Here we're building up a query that looks like this, a sub query is required because the resulting structure
|
||||
// needs to still contain all of the section rows per user.
|
||||
|
||||
//SELECT *
|
||||
//FROM [umbracoUser]
|
||||
//LEFT JOIN [umbracoUser2app]
|
||||
//ON [umbracoUser].[id] = [umbracoUser2app].[user]
|
||||
//WHERE umbracoUser.id IN (SELECT umbracoUser.id
|
||||
// FROM [umbracoUser]
|
||||
// LEFT JOIN [umbracoUser2app]
|
||||
// ON [umbracoUser].[id] = [umbracoUser2app].[user]
|
||||
// WHERE umbracoUser2app.app = 'content')
|
||||
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
var innerSql = GetBaseQuery("umbracoUser.id");
|
||||
innerSql.Where("umbracoUser2app.app = " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedValue(sectionAlias));
|
||||
sql.Where(string.Format("umbracoUser.id IN ({0})", innerSql.SQL));
|
||||
|
||||
var dto = Database.FirstOrDefault<UserDto>(sql);
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
return new Profile(dto.Id, dto.UserName);
|
||||
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private IEnumerable<IUser> ConvertFromDtos(IEnumerable<UserDto> dtos)
|
||||
{
|
||||
var foundUserTypes = new Dictionary<short, IUserType>();
|
||||
return dtos.Select(dto =>
|
||||
{
|
||||
//first we need to get the user type
|
||||
IUserType userType;
|
||||
if (foundUserTypes.ContainsKey(dto.Type))
|
||||
{
|
||||
userType = foundUserTypes[dto.Type];
|
||||
}
|
||||
else
|
||||
{
|
||||
userType = _userTypeRepository.Get(dto.Type);
|
||||
//put it in the local cache
|
||||
foundUserTypes.Add(dto.Type, userType);
|
||||
}
|
||||
|
||||
var userFactory = new UserFactory(userType);
|
||||
return userFactory.BuildEntity(dto);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var dto = Database.Query<UserTypeDto>(sql).FirstOrDefault();
|
||||
var dto = Database.FirstOrDefault<UserTypeDto>(sql);
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
@@ -8,7 +8,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal abstract class VersionableRepositoryBase<TId, TEntity> : PetaPocoRepositoryBase<TId, TEntity>
|
||||
internal abstract class VersionableRepositoryBase<TId, TEntity> : PermissionRepository<TId, TEntity>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
protected VersionableRepositoryBase(IDatabaseUnitOfWork work) : base(work)
|
||||
@@ -43,8 +43,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public virtual void DeleteVersion(Guid versionId)
|
||||
{
|
||||
var dto = Database.FirstOrDefault<ContentVersionDto>("WHERE versionId = @VersionId AND newest = @Newest", new { VersionId = versionId, Newest = false });
|
||||
Mandate.That<Exception>(dto != null);
|
||||
var dto = Database.FirstOrDefault<ContentVersionDto>("WHERE versionId = @VersionId", new { VersionId = versionId });
|
||||
if(dto == null) return;
|
||||
|
||||
using (var transaction = Database.GetTransaction())
|
||||
{
|
||||
@@ -56,8 +56,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public virtual void DeleteVersions(int id, DateTime versionDate)
|
||||
{
|
||||
var list = Database.Fetch<ContentVersionDto>("WHERE nodeId = @Id AND VersionDate < @VersionDate", new { Id = id, VersionDate = versionDate });
|
||||
Mandate.That<Exception>(list.Any());
|
||||
var list = Database.Fetch<ContentVersionDto>("WHERE ContentId = @Id AND VersionDate < @VersionDate", new { Id = id, VersionDate = versionDate });
|
||||
if (list.Any() == false) return;
|
||||
|
||||
using (var transaction = Database.GetTransaction())
|
||||
{
|
||||
|
||||
@@ -9,34 +9,6 @@ namespace Umbraco.Core.Persistence
|
||||
/// </summary>
|
||||
public class RepositoryFactory
|
||||
{
|
||||
|
||||
internal virtual ServerRegistrationRepository CreateServerRegistrationRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new ServerRegistrationRepository(
|
||||
uow,
|
||||
NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
internal virtual IUserTypeRepository CreateUserTypeRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new UserTypeRepository(
|
||||
uow,
|
||||
NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
internal virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new UserRepository(
|
||||
uow,
|
||||
NullCacheProvider.Current,
|
||||
CreateUserTypeRepository(uow));
|
||||
}
|
||||
|
||||
internal virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new EntityRepository(uow);
|
||||
}
|
||||
|
||||
public virtual IContentRepository CreateContentRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new ContentRepository(
|
||||
@@ -81,7 +53,7 @@ namespace Umbraco.Core.Persistence
|
||||
return new MediaRepository(
|
||||
uow,
|
||||
RuntimeCacheProvider.Current,
|
||||
CreateMediaTypeRepository(uow));
|
||||
CreateMediaTypeRepository(uow)) { EnsureUniqueNaming = Umbraco.Core.Configuration.UmbracoSettings.EnsureUniqueNaming };
|
||||
}
|
||||
|
||||
public virtual IMediaTypeRepository CreateMediaTypeRepository(IDatabaseUnitOfWork uow)
|
||||
@@ -118,7 +90,39 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public virtual ITemplateRepository CreateTemplateRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new TemplateRepository(uow, NullCacheProvider.Current);
|
||||
return new TemplateRepository(uow, RuntimeCacheProvider.Current);
|
||||
}
|
||||
|
||||
internal virtual ServerRegistrationRepository CreateServerRegistrationRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new ServerRegistrationRepository(
|
||||
uow,
|
||||
NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
internal virtual IUserTypeRepository CreateUserTypeRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new UserTypeRepository(
|
||||
uow,
|
||||
NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
internal virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new UserRepository(
|
||||
uow,
|
||||
NullCacheProvider.Current,
|
||||
CreateUserTypeRepository(uow));
|
||||
}
|
||||
|
||||
internal virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new EntityRepository(uow);
|
||||
}
|
||||
|
||||
internal virtual RecycleBinRepository CreateRecycleBinRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new RecycleBinRepository(uow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,11 +317,10 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
{
|
||||
db.OpenSharedConnection();
|
||||
// Need 4 @ signs as it is regarded as a parameter, @@ escapes it once, @@@@ escapes it twice
|
||||
var lowerCaseFileSystem = db.Fetch<int>("SELECT @@@@Global.lower_case_file_system");
|
||||
var lowerCaseTableNames = db.Fetch<int>("SELECT @@@@Global.lower_case_table_names");
|
||||
|
||||
if(lowerCaseFileSystem.Any() && lowerCaseTableNames.Any())
|
||||
supportsCaseInsensitiveQueries = lowerCaseFileSystem.First() == 1 && lowerCaseTableNames.First() == 1;
|
||||
if(lowerCaseTableNames.Any())
|
||||
supportsCaseInsensitiveQueries = lowerCaseTableNames.First() == 1;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,20 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
public static ISqlSyntaxProvider Provider { get { return new SqlServerSyntaxProvider(); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...)
|
||||
/// </summary>
|
||||
internal enum SqlServerVersionName
|
||||
{
|
||||
Invalid = -1,
|
||||
V7 = 0,
|
||||
V2000 = 1,
|
||||
V2005 = 2,
|
||||
V2008 = 3,
|
||||
V2012 = 4,
|
||||
Other = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an SqlSyntaxProvider for Sql Server
|
||||
/// </summary>
|
||||
@@ -36,6 +50,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
InitColumnTypeMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the version of the current SQL server instance
|
||||
/// </summary>
|
||||
internal Lazy<SqlServerVersionName> VersionName { get; set; }
|
||||
|
||||
public override string GetQuotedTableName(string tableName)
|
||||
{
|
||||
return string.Format("[{0}]", tableName);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using StackExchange.Profiling;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -46,8 +48,7 @@ namespace Umbraco.Core.Persistence
|
||||
// wrap the connection with a profiling connection that tracks timings
|
||||
return new StackExchange.Profiling.Data.ProfiledDbConnection(connection as DbConnection, MiniProfiler.Current);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void OnException(Exception x)
|
||||
{
|
||||
LogHelper.Info<UmbracoDatabase>(x.StackTrace);
|
||||
|
||||
@@ -20,7 +20,6 @@ using File = System.IO.File;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Used to resolve all plugin types and cache them and is also used to instantiate plugin types
|
||||
/// </summary>
|
||||
@@ -37,6 +36,7 @@ namespace Umbraco.Core
|
||||
internal class PluginManager
|
||||
{
|
||||
private readonly ApplicationContext _appContext;
|
||||
private const string CacheKey = "umbraco-plugins.list";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PluginManager with an ApplicationContext instance which ensures that the plugin xml
|
||||
@@ -251,7 +251,7 @@ namespace Umbraco.Core
|
||||
XDocument xml;
|
||||
if (_appContext != null)
|
||||
{
|
||||
xml = _appContext.ApplicationCache.GetCacheItem("umbraco-plugins.list",
|
||||
xml = _appContext.ApplicationCache.GetCacheItem(CacheKey,
|
||||
new TimeSpan(0, 0, 5, 0),
|
||||
() => XDocument.Load(filePath));
|
||||
}
|
||||
@@ -304,18 +304,18 @@ namespace Umbraco.Core
|
||||
|
||||
if (_appContext != null)
|
||||
{
|
||||
_appContext.ApplicationCache.ClearCacheItem("umbraco-plugins.list");
|
||||
_appContext.ApplicationCache.ClearCacheItem(CacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string GetPluginListFilePath()
|
||||
{
|
||||
return Path.Combine(_tempFolder, "umbraco-plugins.list");
|
||||
return Path.Combine(_tempFolder, string.Format("umbraco-plugins.{0}.list", NetworkHelper.FileSafeMachineName));
|
||||
}
|
||||
|
||||
private string GetPluginHashFilePath()
|
||||
{
|
||||
return Path.Combine(_tempFolder, "umbraco-plugins.hash");
|
||||
return Path.Combine(_tempFolder, string.Format("umbraco-plugins.{0}.hash", NetworkHelper.FileSafeMachineName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using StackExchange.Profiling;
|
||||
using StackExchange.Profiling.SqlFormatters;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -52,13 +53,8 @@ namespace Umbraco.Core.Profiling
|
||||
/// <param name="e"></param>
|
||||
void UmbracoApplicationEndRequest(object sender, EventArgs e)
|
||||
{
|
||||
if (GlobalSettings.DebugMode == false) return;
|
||||
var request = TryGetRequest(sender);
|
||||
if (request.Success == false) return;
|
||||
if (request.Result.Url.IsClientSideRequest()) return;
|
||||
if (string.IsNullOrEmpty(request.Result["umbDebug"]) == false)
|
||||
if (CanPerformProfilingAction(sender))
|
||||
{
|
||||
//stop the profiler
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
@@ -70,17 +66,28 @@ namespace Umbraco.Core.Profiling
|
||||
/// <param name="e"></param>
|
||||
void UmbracoApplicationBeginRequest(object sender, EventArgs e)
|
||||
{
|
||||
if (GlobalSettings.DebugMode == false) return;
|
||||
var request = TryGetRequest(sender);
|
||||
if (request.Success == false) return;
|
||||
if (request.Result.Url.IsClientSideRequest()) return;
|
||||
if (string.IsNullOrEmpty(request.Result["umbDebug"]) == false)
|
||||
if (CanPerformProfilingAction(sender))
|
||||
{
|
||||
//start the profiler
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanPerformProfilingAction(object sender)
|
||||
{
|
||||
if (GlobalSettings.DebugMode == false)
|
||||
return false;
|
||||
|
||||
//will not run in medium trust
|
||||
if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High)
|
||||
return false;
|
||||
|
||||
var request = TryGetRequest(sender);
|
||||
if (request.Success == false || request.Result.Url.IsClientSideRequest() || string.IsNullOrEmpty(request.Result["umbDebug"]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Render the UI to display the profiler
|
||||
/// </summary>
|
||||
@@ -103,9 +110,7 @@ namespace Umbraco.Core.Profiling
|
||||
/// </remarks>
|
||||
public IDisposable Step(string name)
|
||||
{
|
||||
if (GlobalSettings.DebugMode == false) return null;
|
||||
|
||||
return MiniProfiler.Current.Step(name);
|
||||
return GlobalSettings.DebugMode == false ? null : MiniProfiler.Current.Step(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,10 +118,8 @@ namespace Umbraco.Core.Profiling
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (GlobalSettings.DebugMode == false) return;
|
||||
//will not run in medium trust
|
||||
if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High) return;
|
||||
|
||||
MiniProfiler.Settings.SqlFormatter = new SqlServerFormatter();
|
||||
MiniProfiler.Settings.StackMaxLength = 5000;
|
||||
MiniProfiler.Start();
|
||||
}
|
||||
|
||||
@@ -129,10 +132,6 @@ namespace Umbraco.Core.Profiling
|
||||
/// </remarks>
|
||||
public void Stop(bool discardResults = false)
|
||||
{
|
||||
if (GlobalSettings.DebugMode == false) return;
|
||||
//will not run in medium trust
|
||||
if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High) return;
|
||||
|
||||
MiniProfiler.Stop(discardResults);
|
||||
}
|
||||
|
||||
@@ -156,6 +155,5 @@ namespace Umbraco.Core.Profiling
|
||||
return new Attempt<HttpRequestBase>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Publishing
|
||||
@@ -7,10 +8,14 @@ namespace Umbraco.Core.Publishing
|
||||
/// </summary>
|
||||
internal class PublishStatus
|
||||
{
|
||||
public IContent ContentItem { get; private set; }
|
||||
public PublishStatusType StatusType { get; internal set; }
|
||||
public PublishStatus()
|
||||
{
|
||||
//initialize
|
||||
InvalidProperties = new List<Property>();
|
||||
}
|
||||
|
||||
public PublishStatus(IContent content, PublishStatusType statusType)
|
||||
: this()
|
||||
{
|
||||
ContentItem = content;
|
||||
StatusType = statusType;
|
||||
@@ -23,7 +28,13 @@ namespace Umbraco.Core.Publishing
|
||||
: this(content, PublishStatusType.Success)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public IContent ContentItem { get; private set; }
|
||||
public PublishStatusType StatusType { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets sets the invalid properties if the status failed due to validation.
|
||||
/// </summary>
|
||||
public IEnumerable<Property> InvalidProperties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
internal static class FormsAuthenticationTicketExtensions
|
||||
{
|
||||
public static UmbracoBackOfficeIdentity CreateUmbracoIdentity(this FormsAuthenticationTicket ticket)
|
||||
{
|
||||
try
|
||||
{
|
||||
//create the Umbraco user identity
|
||||
return new UmbracoBackOfficeIdentity(ticket);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//This might occur if we cannot decrypt the value in which case we'll just ignore it, it will
|
||||
// be handled by the base pages
|
||||
LogHelper.Error(typeof(FormsAuthenticationTicketExtensions), "An error occurred decrypting the user's ticket", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions to create and renew and remove authentication tickets for the Umbraco back office
|
||||
/// </summary>
|
||||
internal static class AuthenticationExtensions
|
||||
{
|
||||
//public static UmbracoBackOfficeIdentity GetCurrentIdentity(this HttpContextBase http)
|
||||
//{
|
||||
// return http.User.Identity as UmbracoBackOfficeIdentity;
|
||||
//}
|
||||
|
||||
//internal static UmbracoBackOfficeIdentity GetCurrentIdentity(this HttpContext http)
|
||||
//{
|
||||
// return new HttpContextWrapper(http).GetCurrentIdentity();
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// This clears the authentication cookie
|
||||
/// </summary>
|
||||
public static void UmbracoLogout(this HttpContextBase http)
|
||||
{
|
||||
Logout(http, UmbracoSettings.AuthCookieName);
|
||||
}
|
||||
|
||||
internal static void UmbracoLogout(this HttpContext http)
|
||||
{
|
||||
new HttpContextWrapper(http).UmbracoLogout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the umbraco authentication ticket
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="userdata"></param>
|
||||
public static void CreateUmbracoAuthTicket(this HttpContextBase http, UserData userdata)
|
||||
{
|
||||
CreateAuthTicket(
|
||||
http,
|
||||
userdata,
|
||||
//This is one full day... this is how Umbraco has always created this cookie, it is setup to always
|
||||
//expire one day from issue and it never gets updated.
|
||||
1440,
|
||||
"/",
|
||||
UmbracoSettings.AuthCookieName,
|
||||
UmbracoSettings.AuthCookieDomain);
|
||||
}
|
||||
|
||||
internal static void CreateUmbracoAuthTicket(this HttpContext http, UserData userdata)
|
||||
{
|
||||
new HttpContextWrapper(http).CreateUmbracoAuthTicket(userdata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the umbraco auth ticket
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <returns></returns>
|
||||
public static FormsAuthenticationTicket GetUmbracoAuthTicket(this HttpContextBase http)
|
||||
{
|
||||
return GetAuthTicket(http, UmbracoSettings.AuthCookieName);
|
||||
}
|
||||
|
||||
internal static FormsAuthenticationTicket GetUmbracoAuthTicket(this HttpContext http)
|
||||
{
|
||||
return new HttpContextWrapper(http).GetUmbracoAuthTicket();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This clears the authentication cookie
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="cookieName"></param>
|
||||
private static void Logout(this HttpContextBase http, string cookieName)
|
||||
{
|
||||
//remove from the request
|
||||
http.Request.Cookies.Remove(cookieName);
|
||||
|
||||
//expire from the response
|
||||
var formsCookie = http.Response.Cookies[cookieName];
|
||||
if (formsCookie != null)
|
||||
{
|
||||
//this will expire immediately and be removed from the browser
|
||||
formsCookie.Expires = DateTime.Now.AddYears(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure there's def an expired cookie
|
||||
http.Response.Cookies.Add(new HttpCookie(cookieName) { Expires = DateTime.Now.AddYears(-1) });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In v6 this is a custom cookie, in v7 this is a real formsauth cookie.
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="cookieName"></param>
|
||||
/// <returns></returns>
|
||||
private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName)
|
||||
{
|
||||
var formsCookie = http.Request.Cookies[cookieName];
|
||||
if (formsCookie == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
//get the cookie value
|
||||
var cookieVal = formsCookie.Value.DecryptWithMachineKey();
|
||||
|
||||
//here we need to see if the cookie val can be serialized into UserData, if not it means it's probably an old cookie
|
||||
var deserialized = JsonConvert.DeserializeObject<UserData>(cookieVal);
|
||||
|
||||
//in v6, we're not using real FormsAuth but our own custom cookie and then we just return a custom FormsAuth ticket
|
||||
// for this request.
|
||||
return new FormsAuthenticationTicket(
|
||||
4,
|
||||
deserialized.RealName,
|
||||
DateTime.Now,
|
||||
DateTime.Now.AddMinutes(GlobalSettings.TimeOutInMinutes),
|
||||
false,
|
||||
cookieVal,
|
||||
"/");
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//occurs when decryption fails
|
||||
http.Logout(cookieName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a custom umbraco auth cookie with the data specified
|
||||
/// </summary>
|
||||
/// <param name="http">The HTTP.</param>
|
||||
/// <param name="userData">The user data.</param>
|
||||
/// <param name="minutesPersisted">The minutes persisted.</param>
|
||||
/// <param name="cookiePath">The cookie path.</param>
|
||||
/// <param name="cookieName">Name of the cookie.</param>
|
||||
/// <param name="cookieDomain">The cookie domain.</param>
|
||||
private static void CreateAuthTicket(this HttpContextBase http,
|
||||
UserData userData,
|
||||
int minutesPersisted,
|
||||
string cookiePath,
|
||||
string cookieName,
|
||||
string cookieDomain)
|
||||
{
|
||||
var cookie = new HttpCookie(cookieName);
|
||||
|
||||
if (GlobalSettings.UseSSL)
|
||||
cookie.Secure = true;
|
||||
|
||||
//ensure http only, this should only be able to be accessed via the server
|
||||
cookie.HttpOnly = true;
|
||||
cookie.Path = cookiePath;
|
||||
cookie.Domain = cookieDomain;
|
||||
cookie.Expires = DateTime.Now.AddMinutes(minutesPersisted);
|
||||
|
||||
//serialize the user data
|
||||
var json = JsonConvert.SerializeObject(userData);
|
||||
//encrypt it
|
||||
var encTicket = json.EncryptWithMachineKey();
|
||||
|
||||
//set the cookie value
|
||||
cookie.Value = encTicket;
|
||||
|
||||
http.Response.Cookies.Set(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom user identity for the Umbraco backoffice
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All values are lazy loaded for performance reasons as the constructor is called for every single request
|
||||
/// </remarks>
|
||||
internal class UmbracoBackOfficeIdentity : FormsIdentity
|
||||
{
|
||||
public UmbracoBackOfficeIdentity(FormsAuthenticationTicket ticket)
|
||||
: base(ticket)
|
||||
{
|
||||
UserData = ticket.UserData;
|
||||
EnsureDeserialized();
|
||||
}
|
||||
|
||||
protected readonly string UserData;
|
||||
internal UserData DeserializedData;
|
||||
|
||||
public string UserContextId
|
||||
{
|
||||
get { return DeserializedData.UserContextId; }
|
||||
}
|
||||
|
||||
public int StartContentNode
|
||||
{
|
||||
get { return DeserializedData.StartContentNode; }
|
||||
}
|
||||
|
||||
public int StartMediaNode
|
||||
{
|
||||
get { return DeserializedData.StartMediaNode; }
|
||||
}
|
||||
|
||||
public string[] AllowedApplications
|
||||
{
|
||||
get { return DeserializedData.AllowedApplications; }
|
||||
}
|
||||
|
||||
public object Id
|
||||
{
|
||||
get { return DeserializedData.Id; }
|
||||
}
|
||||
|
||||
public string RealName
|
||||
{
|
||||
get { return DeserializedData.RealName; }
|
||||
}
|
||||
|
||||
public string Culture
|
||||
{
|
||||
get { return DeserializedData.Culture; }
|
||||
}
|
||||
|
||||
//public int SessionTimeout
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// EnsureDeserialized();
|
||||
// return DeserializedData.SessionTimeout;
|
||||
// }
|
||||
//}
|
||||
|
||||
public string[] Roles
|
||||
{
|
||||
get { return DeserializedData.Roles; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will ensure we only deserialize once
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For performance reasons, we'll also check if there's an http context available,
|
||||
/// if so, we'll chuck our instance in there so that we only deserialize once per request.
|
||||
/// </remarks>
|
||||
protected void EnsureDeserialized()
|
||||
{
|
||||
if (DeserializedData != null)
|
||||
return;
|
||||
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
//check if we've already done this in this request
|
||||
var data = HttpContext.Current.Items[typeof(UmbracoBackOfficeIdentity)] as UserData;
|
||||
if (data != null)
|
||||
{
|
||||
DeserializedData = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(UserData))
|
||||
{
|
||||
throw new NullReferenceException("The " + typeof(UserData) + " found in the ticket cannot be empty");
|
||||
}
|
||||
DeserializedData = JsonConvert.DeserializeObject<UserData>(UserData);
|
||||
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
HttpContext.Current.Items[typeof (UmbracoBackOfficeIdentity)] = DeserializedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Data structure used to store information in the authentication cookie
|
||||
/// </summary>
|
||||
[DataContract(Name = "userData", Namespace = "")]
|
||||
internal class UserData
|
||||
{
|
||||
public UserData()
|
||||
{
|
||||
AllowedApplications = new string[] {};
|
||||
Roles = new string[] {};
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// When their session is going to expire (in ticks)
|
||||
///// </summary>
|
||||
//[DataMember(Name = "timeout")]
|
||||
//public long Timeout { get; set; }
|
||||
|
||||
[DataMember(Name = "userContextId")]
|
||||
public string UserContextId { get; set; }
|
||||
|
||||
[DataMember(Name = "id")]
|
||||
public object Id { get; set; }
|
||||
|
||||
[DataMember(Name = "roles")]
|
||||
public string[] Roles { get; set; }
|
||||
|
||||
[DataMember(Name = "username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string RealName { get; set; }
|
||||
|
||||
[DataMember(Name = "startContent")]
|
||||
public int StartContentNode { get; set; }
|
||||
|
||||
[DataMember(Name = "startMedia")]
|
||||
public int StartMediaNode { get; set; }
|
||||
|
||||
[DataMember(Name = "allowedApps")]
|
||||
public string[] AllowedApplications { get; set; }
|
||||
|
||||
[DataMember(Name = "culture")]
|
||||
public string Culture { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,17 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Auditing;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Publishing;
|
||||
|
||||
@@ -88,7 +87,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id);
|
||||
|
||||
return content;
|
||||
}
|
||||
@@ -123,7 +122,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IContent>(content, false, contentTypeAlias, parent), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id);
|
||||
|
||||
return content;
|
||||
}
|
||||
@@ -163,7 +162,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id);
|
||||
|
||||
return content;
|
||||
}
|
||||
@@ -203,7 +202,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IContent>(content, false, contentTypeAlias, parent), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", content.CreatorId, content.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id);
|
||||
|
||||
return content;
|
||||
}
|
||||
@@ -252,6 +251,17 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<IContent> GetPublishedContentOfContentType(int id)
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IContent>.Builder.Where(x => x.ContentTypeId == id);
|
||||
var contents = repository.GetByPublishedVersion(query);
|
||||
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects by Level
|
||||
/// </summary>
|
||||
@@ -433,6 +443,19 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all published content items
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<IContent> GetAllPublished()
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IContent>.Builder.Where(x => x.Trashed == false);
|
||||
return repository.GetByPublishedVersion(query);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects, which has an expiration date less than or equal to today.
|
||||
/// </summary>
|
||||
@@ -988,27 +1011,29 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public void EmptyRecycleBin()
|
||||
{
|
||||
//TODO: Why don't we have a base class to share between MediaService/ContentService as some of this is exacty the same?
|
||||
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var query = Query<IContent>.Builder.Where(x => x.Trashed == true);
|
||||
var contents = repository.GetByQuery(query).OrderByDescending(x => x.Level);
|
||||
List<int> ids;
|
||||
List<string> files;
|
||||
bool success;
|
||||
var nodeObjectType = new Guid(Constants.ObjectTypes.Document);
|
||||
|
||||
foreach (var content in contents)
|
||||
using (var repository = _repositoryFactory.CreateRecycleBinRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IContent>(content), this))
|
||||
continue;
|
||||
ids = repository.GetIdsOfItemsInRecycleBin(nodeObjectType);
|
||||
files = repository.GetFilesInRecycleBin(nodeObjectType);
|
||||
|
||||
repository.Delete(content);
|
||||
if (EmptyingRecycleBin.IsRaisedEventCancelled(new RecycleBinEventArgs(nodeObjectType, ids, files), this))
|
||||
return;
|
||||
|
||||
Deleted.RaiseEvent(new DeleteEventArgs<IContent>(content, false), this);
|
||||
success = repository.EmptyRecycleBin(nodeObjectType);
|
||||
if (success)
|
||||
repository.DeleteFiles(files);
|
||||
}
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -20);
|
||||
EmptiedRecycleBin.RaiseEvent(new RecycleBinEventArgs(nodeObjectType, ids, files, success), this);
|
||||
}
|
||||
Audit.Add(AuditTypes.Delete, "Empty Content Recycle Bin performed by user", 0, -20);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1124,6 +1149,9 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Audit.Add(AuditTypes.Copy, "Copy Content performed by user", content.WriterId, content.Id);
|
||||
|
||||
|
||||
//TODO: Don't think we need this here because cache should be cleared by the event listeners
|
||||
// and the correct ICacheRefreshers!?
|
||||
RuntimeCacheProvider.Current.Clear();
|
||||
|
||||
return copy;
|
||||
@@ -1352,46 +1380,45 @@ namespace Umbraco.Core.Services
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
if (contentTypeIds.Any() == false)
|
||||
{
|
||||
//Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!)
|
||||
uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)");
|
||||
|
||||
//get all content items that are published
|
||||
// Consider creating a Path query instead of recursive method:
|
||||
// var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith("-1"));
|
||||
var rootContent = GetRootContent();
|
||||
foreach (var content in rootContent.Where(content => content.Published))
|
||||
{
|
||||
list.Add(content);
|
||||
list.AddRange(GetPublishedDescendants(content));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var id in contentTypeIds)
|
||||
{
|
||||
//first we'll clear out the data from the cmsContentXml table for this type
|
||||
uow.Database.Execute(@"delete from cmsContentXml where nodeId in
|
||||
(select cmsDocument.nodeId from cmsDocument
|
||||
inner join cmsContent on cmsDocument.nodeId = cmsContent.nodeId
|
||||
where published = 1 and contentType = @contentTypeId)", new {contentTypeId = id});
|
||||
//First we're going to get the data that needs to be inserted before clearing anything, this
|
||||
//ensures that we don't accidentally leave the content xml table empty if something happens
|
||||
//during the lookup process.
|
||||
|
||||
//now get all published content objects of this type and add to the list
|
||||
list.AddRange(GetContentOfContentType(id).Where(content => content.Published));
|
||||
}
|
||||
}
|
||||
list.AddRange(contentTypeIds.Any() == false
|
||||
? GetAllPublished()
|
||||
: contentTypeIds.SelectMany(GetPublishedContentOfContentType));
|
||||
|
||||
var xmlItems = new List<ContentXmlDto>();
|
||||
foreach (var c in list)
|
||||
{
|
||||
//generate the xml
|
||||
var xml = c.ToXml();
|
||||
//create the dto to insert
|
||||
var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) };
|
||||
//insert it into the database
|
||||
uow.Database.Insert(poco);
|
||||
xmlItems.Add(new ContentXmlDto {NodeId = c.Id, Xml = xml.ToString(SaveOptions.None)});
|
||||
}
|
||||
|
||||
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
|
||||
using (var tr = uow.Database.GetTransaction())
|
||||
{
|
||||
if (contentTypeIds.Any() == false)
|
||||
{
|
||||
//Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!)
|
||||
uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var id in contentTypeIds)
|
||||
{
|
||||
//first we'll clear out the data from the cmsContentXml table for this type
|
||||
uow.Database.Execute(@"delete from cmsContentXml where nodeId in
|
||||
(select cmsDocument.nodeId from cmsDocument
|
||||
inner join cmsContent on cmsDocument.nodeId = cmsContent.nodeId
|
||||
where published = 1 and contentType = @contentTypeId)", new { contentTypeId = id });
|
||||
}
|
||||
}
|
||||
|
||||
//bulk insert it into the database
|
||||
uow.Database.BulkInsertRecords(xmlItems, tr);
|
||||
}
|
||||
}
|
||||
Audit.Add(AuditTypes.Publish, "RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
|
||||
@@ -1435,7 +1462,12 @@ namespace Umbraco.Core.Services
|
||||
LogHelper.Info<ContentService>(
|
||||
string.Format("Content '{0}' with Id '{1}' could not be published because of invalid properties.",
|
||||
content.Name, content.Id));
|
||||
result.Add(new Attempt<PublishStatus>(false, new PublishStatus(content, PublishStatusType.FailedContentInvalid)));
|
||||
result.Add(
|
||||
new Attempt<PublishStatus>(false,
|
||||
new PublishStatus(content, PublishStatusType.FailedContentInvalid)
|
||||
{
|
||||
InvalidProperties = ((ContentBase) content).LastInvalidProperties
|
||||
}));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1551,6 +1583,8 @@ namespace Umbraco.Core.Services
|
||||
publishStatus.StatusType = CheckAndLogIsPublishable(content);
|
||||
//Content contains invalid property values and can therefore not be published - fire event?
|
||||
publishStatus.StatusType = CheckAndLogIsValid(content);
|
||||
//set the invalid properties (if there are any)
|
||||
publishStatus.InvalidProperties = ((ContentBase) content).LastInvalidProperties;
|
||||
|
||||
//if we're still successful, then publish using the strategy
|
||||
if (publishStatus.StatusType == PublishStatusType.Success)
|
||||
@@ -1851,7 +1885,7 @@ namespace Umbraco.Core.Services
|
||||
/// Occurs after Create
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please note that the Content object has been created, but not saved
|
||||
/// Please note that the Content object has been created, but might not have been saved
|
||||
/// so it does not have an identity yet (meaning no Id has been set).
|
||||
/// </remarks>
|
||||
public static event TypedEventHandler<IContentService, NewEventArgs<IContent>> Created;
|
||||
@@ -1905,6 +1939,16 @@ namespace Umbraco.Core.Services
|
||||
/// Occurs after Send to Publish
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SendToPublishEventArgs<IContent>> SentToPublish;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before the Recycle Bin is emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, RecycleBinEventArgs> EmptyingRecycleBin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after the Recycle Bin has been Emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, RecycleBinEventArgs> EmptiedRecycleBin;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines part of the UserService, which is specific to methods used by the membership provider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl.
|
||||
/// </remarks>
|
||||
internal interface IMembershipUserService : IService
|
||||
{
|
||||
IMembershipUser CreateMembershipUser(string name, string login, string password, IUserType userType, string email = "");
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,10 @@ namespace Umbraco.Core.Services
|
||||
/// <returns><see cref="IProfile"/></returns>
|
||||
IProfile GetProfileById(int id);
|
||||
|
||||
IProfile GetProfileByUserName(string username);
|
||||
IUser GetUserByUserName(string username);
|
||||
IUser GetUserById(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an IUserType by its Alias
|
||||
/// </summary>
|
||||
@@ -28,16 +32,17 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="name">Name of the UserType to retrieve</param>
|
||||
/// <returns><see cref="IUserType"/></returns>
|
||||
IUserType GetUserTypeByName(string name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines part of the UserService, which is specific to methods used by the membership provider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl.
|
||||
/// </remarks>
|
||||
internal interface IMembershipUserService : IService
|
||||
{
|
||||
IMembershipUser CreateUser(string name, string login, string password, IUserType userType, string email = "");
|
||||
/// <summary>
|
||||
/// Saves changes to the user object
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
void SaveUser(IUser user);
|
||||
|
||||
/// <summary>
|
||||
/// This is useful when an entire section is removed from config
|
||||
/// </summary>
|
||||
/// <param name="sectionAlias"></param>
|
||||
void DeleteSectionFromAllUsers(string sectionAlias);
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,11 @@ using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Publishing;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Represents the Media Service, which is an easy access to operations involving <see cref="IMedia"/>
|
||||
/// </summary>
|
||||
public class MediaService : IMediaService
|
||||
@@ -63,9 +64,9 @@ namespace Umbraco.Core.Services
|
||||
|
||||
media.CreatorId = userId;
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parentId), this);
|
||||
Created.RaiseEvent(new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parentId), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created", name), media.CreatorId, media.Id);
|
||||
|
||||
return media;
|
||||
}
|
||||
@@ -98,7 +99,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parent), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created", name), media.CreatorId, media.Id);
|
||||
|
||||
return media;
|
||||
}
|
||||
@@ -142,7 +143,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parentId), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id);
|
||||
|
||||
return media;
|
||||
}
|
||||
@@ -186,7 +187,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Created.RaiseEvent(new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parent), this);
|
||||
|
||||
Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id);
|
||||
Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id);
|
||||
|
||||
return media;
|
||||
}
|
||||
@@ -536,27 +537,29 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public void EmptyRecycleBin()
|
||||
{
|
||||
//TODO: Why don't we have a base class to share between MediaService/ContentService as some of this is exacty the same?
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
List<int> ids;
|
||||
List<string> files;
|
||||
bool success;
|
||||
var nodeObjectType = new Guid(Constants.ObjectTypes.Media);
|
||||
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
var query = Query<IMedia>.Builder.Where(x => x.Trashed == true);
|
||||
var contents = repository.GetByQuery(query).OrderByDescending(x => x.Level);
|
||||
using (var repository = _repositoryFactory.CreateRecycleBinRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
ids = repository.GetIdsOfItemsInRecycleBin(nodeObjectType);
|
||||
files = repository.GetFilesInRecycleBin(nodeObjectType);
|
||||
|
||||
foreach (var content in contents)
|
||||
{
|
||||
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMedia>(content), this))
|
||||
continue;
|
||||
if (EmptyingRecycleBin.IsRaisedEventCancelled(new RecycleBinEventArgs(nodeObjectType, ids, files), this))
|
||||
return;
|
||||
|
||||
repository.Delete(content);
|
||||
success = repository.EmptyRecycleBin(nodeObjectType);
|
||||
if (success)
|
||||
repository.DeleteFiles(files);
|
||||
}
|
||||
|
||||
Deleted.RaiseEvent(new DeleteEventArgs<IMedia>(content, false), this);
|
||||
}
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -20);
|
||||
EmptiedRecycleBin.RaiseEvent(new RecycleBinEventArgs(nodeObjectType, ids, files, success), this);
|
||||
}
|
||||
Audit.Add(AuditTypes.Delete, "Empty Media Recycle Bin performed by user", 0, -21);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -875,15 +878,16 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
var xmlItems = new List<ContentXmlDto>();
|
||||
foreach (var c in list)
|
||||
{
|
||||
//generate the xml
|
||||
var xml = c.ToXml();
|
||||
//create the dto to insert
|
||||
var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) };
|
||||
//insert it into the database
|
||||
uow.Database.Insert(poco);
|
||||
xmlItems.Add(new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) });
|
||||
}
|
||||
//bulk insert it into the database
|
||||
uow.Database.BulkInsertRecords(xmlItems);
|
||||
}
|
||||
Audit.Add(AuditTypes.Publish, "RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
|
||||
}
|
||||
@@ -1009,6 +1013,16 @@ namespace Umbraco.Core.Services
|
||||
/// Occurs after Move
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, MoveEventArgs<IMedia>> Moved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before the Recycle Bin is emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, RecycleBinEventArgs> EmptyingRecycleBin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after the Recycle Bin has been Emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, RecycleBinEventArgs> EmptiedRecycleBin;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
@@ -286,7 +287,7 @@ namespace Umbraco.Core.Services
|
||||
var properties = from property in element.Elements()
|
||||
where property.Attribute("isDoc") == null
|
||||
select property;
|
||||
|
||||
|
||||
IContent content = parent == null
|
||||
? new Content(nodeName, parentId, contentType)
|
||||
{
|
||||
@@ -303,12 +304,33 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
string propertyTypeAlias = isLegacySchema ? property.Attribute("alias").Value : property.Name.LocalName;
|
||||
if (content.HasProperty(propertyTypeAlias))
|
||||
content.SetValue(propertyTypeAlias, property.Value);
|
||||
{
|
||||
var propertyValue = property.Value;
|
||||
|
||||
var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias);
|
||||
if (propertyType != null && propertyType.DataTypeId == new Guid(Constants.PropertyEditors.CheckBoxList))
|
||||
{
|
||||
var database = ApplicationContext.Current.DatabaseContext.Database;
|
||||
var dtos = database.Fetch<DataTypePreValueDto>("WHERE datatypeNo" + "deId = @Id", new { Id = propertyType.DataTypeDefinitionId });
|
||||
|
||||
var propertyValueList = new List<string>();
|
||||
foreach (var preValue in propertyValue.Split(','))
|
||||
{
|
||||
propertyValueList.Add(dtos.Single(x => x.Value == preValue).Id.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
propertyValue = string.Join(",", propertyValueList.ToArray());
|
||||
}
|
||||
|
||||
content.SetValue(propertyTypeAlias, propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentTypes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user