Compare commits
465 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f1c69bc13 | |||
| 4740e5f0c7 | |||
| 4b36dcc13b | |||
| b92f5c4719 | |||
| 11441fb1b1 | |||
| 377879d081 | |||
| 738cc81439 | |||
| 14ba484fc1 | |||
| 0f894da7a6 | |||
| 52640a94ee | |||
| 133e2fdba7 | |||
| e6ef1c2506 | |||
| 7718adfca1 | |||
| afe3ace32e | |||
| 9a0679d41c | |||
| a54b2297c7 | |||
| 871e390c5e | |||
| 34875855db | |||
| 6c0fb765f0 | |||
| 69367e0cda | |||
| 15bbe1b829 | |||
| f95ec4d8f4 | |||
| 4a6f0a791e | |||
| 3779ba38cb | |||
| 2c6eeb7193 | |||
| 6c5849cca9 | |||
| 671b94dd06 | |||
| bc9dc911ed | |||
| cc8298d83c | |||
| 42dd48a3d1 | |||
| d97a98f45d | |||
| 8d892bd099 | |||
| 886ab32f27 | |||
| 937b308444 | |||
| ee4c0cbfc3 | |||
| 3ba9f87d93 | |||
| 626f21aecd | |||
| 2f6637dcc4 | |||
| 21de6dfd00 | |||
| a3b0714a9d | |||
| b9975ad1f5 | |||
| b63135771e | |||
| bc828176d1 | |||
| 13dcfbbd2b | |||
| 3d8138f3ef | |||
| e67f85695c | |||
| 088253ebfd | |||
| dc542a4ae7 | |||
| 219a46f736 | |||
| 7599a68c90 | |||
| a5522188ca | |||
| 36bc2473bf | |||
| abf8aa7ea3 | |||
| e0da6f2dec | |||
| 8a8b09141f | |||
| 18ea5b3768 | |||
| 0e392843dd | |||
| faaa408201 | |||
| 9514654ca4 | |||
| e34e63db51 | |||
| c6c07b3861 | |||
| 6f39f190a4 | |||
| e2c06852d5 | |||
| f45d25c09a | |||
| 2ec96a9123 | |||
| 2cea2e1b41 | |||
| 59f711f802 | |||
| 0d4ee6e5d1 | |||
| f9319c0db9 | |||
| 3ff4930e8a | |||
| ee3fb3745c | |||
| b512376c5c | |||
| d97ae3fbd2 | |||
| a3e4eaa2e4 | |||
| a2df4ea742 | |||
| 79f7ea60f2 | |||
| 52aac5ba10 | |||
| 63a4e00f52 | |||
| 5df30ac0cc | |||
| cb8c1a31cb | |||
| 8ad4ee4e58 | |||
| 6c77749e32 | |||
| 4170713e29 | |||
| 8917a5ad92 | |||
| 49110ffaf7 | |||
| a3bca42117 | |||
| 7919a39bb6 | |||
| 80488e012f | |||
| 0856173c9c | |||
| 98832357bf | |||
| 0843388a41 | |||
| 71e6ea70b2 | |||
| a87b37d6ca | |||
| 0594bafa5b | |||
| 2214916fed | |||
| 8618947d62 | |||
| 912a111f14 | |||
| 47c7f9bfda | |||
| d844c33a2d | |||
| fac71dfd11 | |||
| 1e6b80e45e | |||
| 31d3b88a5b | |||
| de14a6dad7 | |||
| e3102cf95b | |||
| ce97350d76 | |||
| a5b597e2f7 | |||
| 2b76d4b22d | |||
| d7b732ba4b | |||
| 2d3cf3da23 | |||
| 2ff5b63089 | |||
| 530f88aa11 | |||
| c42e957c4c | |||
| 659cba563a | |||
| d255b3a6a3 | |||
| ba7713fb93 | |||
| d5498cc66e | |||
| a023ddb76b | |||
| f7c49758e1 | |||
| 95a834b8b6 | |||
| 5d71e08922 | |||
| e51b43084b | |||
| f299fe76e4 | |||
| 128cceaf91 | |||
| ccedd6757b | |||
| a94f61702f | |||
| 2b2cf48fdb | |||
| 858135175e | |||
| de8b69a399 | |||
| 27e6e9b91c | |||
| 1c13a896eb | |||
| f8d6c85779 | |||
| 09b7186713 | |||
| c6389852ea | |||
| 49f10ccd32 | |||
| a8b037f2e0 | |||
| 3c38b335a6 | |||
| 3a8bbcc8fc | |||
| 1ecb864e5f | |||
| d281f97659 | |||
| 38761e1413 | |||
| f85f418d52 | |||
| 0184b396be | |||
| 8b2e2f8447 | |||
| 2fc8244f71 | |||
| 7c5b6851a1 | |||
| a2ebb1b06f | |||
| 196ce492be | |||
| a5848a17c5 | |||
| 31535e3bd8 | |||
| dfc3e468c4 | |||
| 90ec849b5f | |||
| 30f147a9d3 | |||
| e7a238b2bc | |||
| 17c055472d | |||
| dd4827493b | |||
| 572e23d307 | |||
| febff1560e | |||
| 7b14e8eacc | |||
| 881d922f30 | |||
| 8b358da2f0 | |||
| 19d96ac0e2 | |||
| 3a33a39411 | |||
| 883a662240 | |||
| e6908c7678 | |||
| 0b8b413f2f | |||
| 38a5b49ea3 | |||
| 6a43972267 | |||
| d102db795e | |||
| 6f2a7180d8 | |||
| 6cec665f7d | |||
| d0cd8ba346 | |||
| 16bce4248d | |||
| a2e9ec0412 | |||
| 11c9abf359 | |||
| 4b942dcc32 | |||
| 12b7a9d874 | |||
| ec4eeb5de6 | |||
| f676ce6ac2 | |||
| aa0bb166a8 | |||
| 60a81ac879 | |||
| a9bc67538d | |||
| c0a7a9a427 | |||
| df909983db | |||
| 1a9e5f20a8 | |||
| a90f32035d | |||
| 33b09c80aa | |||
| b2980cedc6 | |||
| 34b7f86b8b | |||
| 130f6d3a42 | |||
| 3ff9d6adbc | |||
| fd5de171c9 | |||
| 45ad51b8df | |||
| b02657f32c | |||
| 9f3784aaed | |||
| 2ab2a5a9f3 | |||
| 2d83ba06a5 | |||
| 06d5c60803 | |||
| fe18722e1f | |||
| c2e7fc2f10 | |||
| e31dd688bf | |||
| 5060dab7a5 | |||
| c8fa22c767 | |||
| 418a29cbbf | |||
| 0b907d4ae3 | |||
| 765ed9687a | |||
| 66e7a6322a | |||
| b595ec4d88 | |||
| 7425e070d2 | |||
| 3cb70f67c9 | |||
| a029d98209 | |||
| db6b8930ab | |||
| 4d8a3f4453 | |||
| f1f5374b81 | |||
| 961928105c | |||
| c6229f131a | |||
| ff3aebd4b1 | |||
| ad6e4ae745 | |||
| b462082ed9 | |||
| 95f120e98f | |||
| a42bd4dbf0 | |||
| 21d6209793 | |||
| 9ee1584ede | |||
| a3d77b7e72 | |||
| 14e1e741ed | |||
| 94f72b3938 | |||
| 20ab14beb4 | |||
| e14e5bee95 | |||
| 9e1364db42 | |||
| 1902bc4807 | |||
| a264342bcc | |||
| 08660704cc | |||
| 4c13dbf8b7 | |||
| 9a10863760 | |||
| 74e414d858 | |||
| 6a1072e918 | |||
| fb1ea4b880 | |||
| cbe566302c | |||
| 4ace6e10db | |||
| bd32c7cc4a | |||
| 25a0c595e7 | |||
| 3d3be5ecbc | |||
| 1730005a58 | |||
| b9257fceeb | |||
| e2f9b1c7d9 | |||
| 03b1c9e5ee | |||
| 85098b84f7 | |||
| 936be3934b | |||
| ed8f22c371 | |||
| 969abb5057 | |||
| 21c0152131 | |||
| 86430f1b01 | |||
| 55247fe31d | |||
| 6ac724951b | |||
| b0dfc90557 | |||
| b09a743beb | |||
| 2ed4206f93 | |||
| a806f40093 | |||
| 4283734a94 | |||
| 6c20815210 | |||
| a371ebe841 | |||
| 92a32a8f8d | |||
| bb1a47aa81 | |||
| 6126a52156 | |||
| 24020cfea3 | |||
| 1b2f0246e8 | |||
| 9ec30860b0 | |||
| 19784e04bf | |||
| 3ee4399fca | |||
| 06206c60d9 | |||
| 5937a7e976 | |||
| f64b8d680d | |||
| f9663a9e80 | |||
| c604cc5c2f | |||
| dc42c1ebb7 | |||
| 92efd6d645 | |||
| 6560865f61 | |||
| 8f53bea3d7 | |||
| 89f7a31bef | |||
| 424b8cd408 | |||
| 65898411e7 | |||
| 759a3619ca | |||
| 520ebc3394 | |||
| e6bb999d83 | |||
| cdd1a0a4cb | |||
| 5e0fbe5634 | |||
| e5b3ca1907 | |||
| efb4a6652d | |||
| 9b43a86b94 | |||
| 1090248223 | |||
| 880077f6e9 | |||
| ffc1b62b8a | |||
| 17f2602ca2 | |||
| a71e255468 | |||
| f2458b833e | |||
| 2b2d986371 | |||
| 1ed21a3ffb | |||
| 1f32373bd2 | |||
| 5b1c626714 | |||
| ce30153830 | |||
| 727f3a804d | |||
| be7179b962 | |||
| 7eee954d4d | |||
| 36cb078073 | |||
| 8b6171bb13 | |||
| 917d5b39ec | |||
| 65f809a0fb | |||
| bad4d0c6e6 | |||
| 2a1e3dc2f8 | |||
| 5199a5dc99 | |||
| 2e8a8e3623 | |||
| 1ec74a5ae8 | |||
| 6c02eadd88 | |||
| 60c4307f2e | |||
| e1c0e70d07 | |||
| 9ddee615cc | |||
| 2c88c7c971 | |||
| 9e361b7669 | |||
| e64eb05dcd | |||
| 0898638e33 | |||
| 5dd9510d69 | |||
| 229b770447 | |||
| 434e194ba7 | |||
| 9c96b247be | |||
| 74f6a34651 | |||
| 7dee2802fe | |||
| 15d25ebb3d | |||
| fbe388a616 | |||
| 891955f164 | |||
| f7103c97cf | |||
| a90e96d066 | |||
| 4e56d88eb5 | |||
| 770514919d | |||
| 31a5951626 | |||
| 76b7b131a9 | |||
| 4602a6b989 | |||
| e27a0e2bd2 | |||
| e5c7a35e8f | |||
| 6277bc379e | |||
| 2bb279d8f7 | |||
| a8f87d6dc1 | |||
| bb350035ef | |||
| 6f6c33a721 | |||
| 7451e83c73 | |||
| 79e35d5c06 | |||
| 59196020bb | |||
| fc1e4f959e | |||
| 146aca31a0 | |||
| c928769911 | |||
| cdbc02354d | |||
| bd3cdd83f6 | |||
| 5a757f129c | |||
| 987c4401a3 | |||
| dee067d071 | |||
| d901f4d4f1 | |||
| 081457ddc8 | |||
| 4f98a1b0c8 | |||
| 7668c3bafa | |||
| 5238293c09 | |||
| 226e35d65a | |||
| 05f7ede26a | |||
| ea638c37b7 | |||
| bfa7ab788b | |||
| 5a15c93195 | |||
| 77777c8007 | |||
| 85e2c3e5b1 | |||
| 47d124a41b | |||
| ca56539c65 | |||
| 8b25210895 | |||
| 9c4dc838a5 | |||
| 57511b9c5a | |||
| cdda69c4cd | |||
| 472ef6e48c | |||
| 9ff74b39f8 | |||
| 5aa01e78ab | |||
| a21bee6683 | |||
| 0cff484113 | |||
| 3e9afcfc41 | |||
| 830fde372e | |||
| ac2a4a1e0a | |||
| 5ffca11c5b | |||
| 35f65a2c64 | |||
| e5320f0abd | |||
| 965a7fc500 | |||
| abbf8aeb42 | |||
| 328ada2f38 | |||
| 767252cdf1 | |||
| 010d47c68b | |||
| 56f1062db8 | |||
| a11e947f96 | |||
| 0be07f467f | |||
| 1a9ced80f9 | |||
| 33599a15a2 | |||
| 42f9427e24 | |||
| e0878fdd9f | |||
| fb3bad90d6 | |||
| 590838302b | |||
| c9aac96c1a | |||
| bbdce28951 | |||
| cf6ecf5bc9 | |||
| 4df79ce55b | |||
| 15d2021786 | |||
| a8f6b9eeb9 | |||
| 3d4b403c0e | |||
| 957760d29a | |||
| 928f7a1ed0 | |||
| a7603747a6 | |||
| e7754213f1 | |||
| ea82e9c67e | |||
| 182cc1ace7 | |||
| 205b9889f2 | |||
| 4a58fbfa1d | |||
| af534e87f3 | |||
| 53c753b8d9 | |||
| a8a2c706ba | |||
| a3d674f574 | |||
| 9a66948857 | |||
| 82c784d560 | |||
| 30095000e6 | |||
| 5d224cca9b | |||
| c5c1a7ddfc | |||
| b5022bb183 | |||
| 2e9bc3bb2b | |||
| 98e487753f | |||
| ce15d46a5e | |||
| 02ffebde49 | |||
| 2e06c61a4f | |||
| 36d6e4b4da | |||
| 725b938de1 | |||
| 042973ce19 | |||
| f9022d8189 | |||
| 7f4a98883b | |||
| 8ff0307a95 | |||
| 917c514fab | |||
| 2ef4b77bd9 | |||
| c7ac4eea2f | |||
| fae9fc6e2d | |||
| 000e278e48 | |||
| bcfb562dde | |||
| 3ca8b356ee | |||
| 6c32cf9529 | |||
| d925f856ae | |||
| 3de2196e01 | |||
| ac2d010df3 | |||
| eb4317a9c1 | |||
| c3caf7ff04 | |||
| f9cae9c4d8 | |||
| dd16af0989 | |||
| c118990a4c | |||
| 7a541ba489 | |||
| a785cd1569 | |||
| 1e786ac311 | |||
| 0c28e01347 | |||
| 08566f32de | |||
| 16ec58f338 | |||
| e8fd6a6ece | |||
| ee4f4440d5 | |||
| f4d060f3eb | |||
| 85cb7fadfc | |||
| ac19ac7a6b | |||
| 0415a31d0e | |||
| 912716f889 | |||
| 5dc9bb752e | |||
| cf435011df | |||
| f63514e8ea | |||
| 1ddf2fa365 |
@@ -103,3 +103,5 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
|
||||
|
||||
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
|
||||
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
@ECHO OFF
|
||||
SET release=7.0.0
|
||||
SET comment=alpha
|
||||
SET comment=beta
|
||||
SET version=%release%
|
||||
|
||||
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core</id>
|
||||
@@ -20,13 +20,14 @@
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" />
|
||||
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.4.6, 2.0.0)" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1]" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.6.5]" />
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.7.0, 1.7.1)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0, 1.7.1)" />
|
||||
<dependency id="AutoMapper" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.7.0, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[5.0.0, 6.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -59,8 +60,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\umbraco.MacroEngines.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.macroRenderings.dll" target="lib\umbraco.macroRenderings.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.macroRenderings.xml" target="lib\umbraco.macroRenderings.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\umbraco.providers.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\Umbraco.Web.UI.dll" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms</id>
|
||||
|
||||
@@ -22,6 +22,12 @@ if ($project) {
|
||||
$umbracoFilesPath = Join-Path $rootPath "UmbracoFiles\*"
|
||||
Copy-Item $umbracoFilesPath $projectDestinationPath -recurse -force
|
||||
|
||||
# Remove Newtonsoft.Json if present in the bin folder
|
||||
# If we don't do this, the correct version never gets copied in
|
||||
$projectDestinationPath = Split-Path $project.FullName -Parent
|
||||
$jsonDllFile = Join-Path $projectDestinationPath "bin\Newtonsoft.Json.dll"
|
||||
Remove-Item $jsonDllFile -Confirm:$false
|
||||
|
||||
# Open readme.txt file
|
||||
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
|
||||
}
|
||||
@@ -28,12 +28,11 @@ namespace Umbraco.Core
|
||||
/// <param name="dbContext"></param>
|
||||
/// <param name="serviceContext"></param>
|
||||
/// <param name="cache"></param>
|
||||
internal ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache)
|
||||
public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache)
|
||||
{
|
||||
if (dbContext == null) throw new ArgumentNullException("dbContext");
|
||||
if (serviceContext == null) throw new ArgumentNullException("serviceContext");
|
||||
if (cache == null) throw new ArgumentNullException("cache");
|
||||
|
||||
_databaseContext = dbContext;
|
||||
_services = serviceContext;
|
||||
ApplicationCache = cache;
|
||||
@@ -43,12 +42,60 @@ namespace Umbraco.Core
|
||||
/// Creates a basic app context
|
||||
/// </summary>
|
||||
/// <param name="cache"></param>
|
||||
internal ApplicationContext(CacheHelper cache)
|
||||
public ApplicationContext(CacheHelper cache)
|
||||
{
|
||||
ApplicationCache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// A method used to set and/or ensure that a global ApplicationContext singleton is created.
|
||||
/// </summary>
|
||||
/// <param name="appContext">
|
||||
/// The instance to set on the global application singleton
|
||||
/// </param>
|
||||
/// <param name="replaceContext">If set to true and the singleton is already set, it will be replaced</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is NOT thread safe
|
||||
/// </remarks>
|
||||
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
|
||||
{
|
||||
if (ApplicationContext.Current != null)
|
||||
{
|
||||
if (!replaceContext)
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
ApplicationContext.Current = appContext;
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method used to create and ensure that a global ApplicationContext singleton is created.
|
||||
/// </summary>
|
||||
/// <param name="cache"></param>
|
||||
/// <param name="replaceContext">
|
||||
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
|
||||
/// startup if for some reason the boot manager is not the umbraco boot manager.
|
||||
/// </param>
|
||||
/// <param name="dbContext"></param>
|
||||
/// <param name="serviceContext"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is NOT thread safe
|
||||
/// </remarks>
|
||||
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
|
||||
{
|
||||
if (ApplicationContext.Current != null)
|
||||
{
|
||||
if (!replaceContext)
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
var ctx = new ApplicationContext(dbContext, serviceContext, cache);
|
||||
ApplicationContext.Current = ctx;
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singleton accessor
|
||||
/// </summary>
|
||||
public static ApplicationContext Current { get; internal set; }
|
||||
@@ -99,7 +146,7 @@ namespace Umbraco.Core
|
||||
//
|
||||
public bool IsConfigured
|
||||
{
|
||||
// fixme - we should not do this - ok for now
|
||||
// todo - we should not do this - ok for now
|
||||
get
|
||||
{
|
||||
return Configured;
|
||||
|
||||
@@ -47,6 +47,5 @@ namespace Umbraco.Core.Cache
|
||||
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
|
||||
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core
|
||||
/// <remarks>
|
||||
/// Good for unit testing
|
||||
/// </remarks>
|
||||
internal static CacheHelper CreateDisabledCacheHelper()
|
||||
public static CacheHelper CreateDisabledCacheHelper()
|
||||
{
|
||||
return new CacheHelper(null, null, null, false);
|
||||
}
|
||||
@@ -74,14 +74,6 @@ namespace Umbraco.Core
|
||||
{
|
||||
}
|
||||
|
||||
internal void ClearStaticCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
if (_enableCache)
|
||||
_staticCache.ClearCacheObjectTypes(predicate);
|
||||
else
|
||||
_nullStaticCache.ClearCacheObjectTypes(predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private ctor used for creating a disabled cache helper
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.BaseRest
|
||||
{
|
||||
[ConfigurationKey("BaseRestExtensions")]
|
||||
|
||||
internal class BaseRestSection : UmbracoConfigurationSection, IBaseRestSection
|
||||
{
|
||||
private const string KeyEnabled = "enabled";
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the configuration key for a section or a group.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class ConfigurationKeyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationKeyAttribute"/> class with a configuration key.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The configurationkey.</param>
|
||||
public ConfigurationKeyAttribute(string configurationKey)
|
||||
{
|
||||
ConfigurationKey = configurationKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration key.
|
||||
/// </summary>
|
||||
public string ConfigurationKey { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class AccessElement : RawXmlConfigurationElement, IAccess
|
||||
{
|
||||
public AccessElement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AccessElement(XElement rawXml)
|
||||
:base(rawXml)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<IAccessItem> Rules
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = new List<AccessItem>();
|
||||
if (RawXml != null)
|
||||
{
|
||||
result.AddRange(RawXml.Elements("deny").Select(x => new AccessItem {Action = AccessType.Deny, Value = x.Value }));
|
||||
result.AddRange(RawXml.Elements("grant").Select(x => new AccessItem { Action = AccessType.Grant, Value = x.Value }));
|
||||
result.AddRange(RawXml.Elements("grantBySection").Select(x => new AccessItem { Action = AccessType.GrantBySection, Value = x.Value }));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class AccessItem : IAccessItem
|
||||
{
|
||||
/// <summary>
|
||||
/// This can be grant, deny or grantBySection
|
||||
/// </summary>
|
||||
public AccessType Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the action
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public enum AccessType
|
||||
{
|
||||
Grant,
|
||||
Deny,
|
||||
GrantBySection
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class AreasElement : ConfigurationElement, IArea
|
||||
{
|
||||
[ConfigurationProperty("area", IsRequired = true)]
|
||||
public InnerTextConfigurationElement<string> AreaName
|
||||
{
|
||||
get { return (InnerTextConfigurationElement<string>)this["area"]; }
|
||||
}
|
||||
|
||||
string IArea.AreaName
|
||||
{
|
||||
get { return AreaName; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class ControlCollection : ConfigurationElementCollection, IEnumerable<IDashboardControl>
|
||||
{
|
||||
internal void Add(ControlElement c)
|
||||
{
|
||||
BaseAdd(c);
|
||||
}
|
||||
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new ControlElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((ControlElement)element).ControlPath;
|
||||
}
|
||||
|
||||
IEnumerator<IDashboardControl> IEnumerable<IDashboardControl>.GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
yield return BaseGet(i) as IDashboardControl;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
|
||||
internal class ControlElement : RawXmlConfigurationElement, IDashboardControl
|
||||
{
|
||||
public bool ShowOnce
|
||||
{
|
||||
get
|
||||
{
|
||||
return RawXml.Attribute("showOnce") == null
|
||||
? false
|
||||
: bool.Parse(RawXml.Attribute("showOnce").Value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddPanel
|
||||
{
|
||||
get
|
||||
{
|
||||
return RawXml.Attribute("addPanel") == null
|
||||
? true
|
||||
: bool.Parse(RawXml.Attribute("addPanel").Value);
|
||||
}
|
||||
}
|
||||
|
||||
public string PanelCaption
|
||||
{
|
||||
get
|
||||
{
|
||||
return RawXml.Attribute("panelCaption") == null
|
||||
? ""
|
||||
: RawXml.Attribute("panelCaption").Value;
|
||||
}
|
||||
}
|
||||
|
||||
public AccessElement Access
|
||||
{
|
||||
get
|
||||
{
|
||||
var access = RawXml.Element("access");
|
||||
if (access == null)
|
||||
{
|
||||
return new AccessElement();
|
||||
}
|
||||
return new AccessElement(access);
|
||||
}
|
||||
}
|
||||
|
||||
public string ControlPath
|
||||
{
|
||||
get
|
||||
{
|
||||
//we need to return the first (and only) text element of the children (wtf... who designed this configuration ! :P )
|
||||
var txt = RawXml.Nodes().OfType<XText>().FirstOrDefault();
|
||||
if (txt == null)
|
||||
{
|
||||
throw new ConfigurationErrorsException("The control element must contain a text node indicating the control path");
|
||||
}
|
||||
return txt.Value.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IAccess IDashboardControl.AccessRights
|
||||
{
|
||||
get { return Access; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class DashboardSection : ConfigurationSection, IDashboardSection
|
||||
{
|
||||
[ConfigurationCollection(typeof(SectionCollection), AddItemName = "section")]
|
||||
[ConfigurationProperty("", IsDefaultCollection = true)]
|
||||
public SectionCollection SectionCollection
|
||||
{
|
||||
get { return (SectionCollection)base[""]; }
|
||||
set { base[""] = value; }
|
||||
}
|
||||
|
||||
IEnumerable<ISection> IDashboardSection.Sections
|
||||
{
|
||||
get { return SectionCollection; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IAccess
|
||||
{
|
||||
IEnumerable<IAccessItem> Rules { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IAccessItem
|
||||
{
|
||||
/// <summary>
|
||||
/// This can be grant, deny or grantBySection
|
||||
/// </summary>
|
||||
AccessType Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the action
|
||||
/// </summary>
|
||||
string Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IArea
|
||||
{
|
||||
string AreaName { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IDashboardControl
|
||||
{
|
||||
bool ShowOnce { get; }
|
||||
|
||||
bool AddPanel { get; }
|
||||
|
||||
string PanelCaption { get; }
|
||||
|
||||
string ControlPath { get; }
|
||||
|
||||
IAccess AccessRights { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IDashboardSection
|
||||
{
|
||||
IEnumerable<ISection> Sections { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IDashboardTab
|
||||
{
|
||||
string Caption { get; }
|
||||
|
||||
IEnumerable<IDashboardControl> Controls { get; }
|
||||
|
||||
IAccess AccessRights { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface ISection
|
||||
{
|
||||
string Alias { get; }
|
||||
|
||||
string Area { get; }
|
||||
|
||||
IEnumerable<IDashboardTab> Tabs { get; }
|
||||
|
||||
IAccess AccessRights { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class SectionCollection : ConfigurationElementCollection, IEnumerable<ISection>
|
||||
{
|
||||
internal void Add(SectionElement c)
|
||||
{
|
||||
BaseAdd(c);
|
||||
}
|
||||
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new SectionElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((SectionElement)element).Alias;
|
||||
}
|
||||
|
||||
IEnumerator<ISection> IEnumerable<ISection>.GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
yield return BaseGet(i) as ISection;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class SectionElement : ConfigurationElement, ISection
|
||||
{
|
||||
[ConfigurationProperty("alias", IsRequired = true)]
|
||||
public string Alias
|
||||
{
|
||||
get { return (string) this["alias"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("areas", IsRequired = true)]
|
||||
public AreasElement Area
|
||||
{
|
||||
get { return (AreasElement)this["areas"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("access")]
|
||||
public AccessElement Access
|
||||
{
|
||||
get { return (AccessElement)this["access"]; }
|
||||
}
|
||||
|
||||
[ConfigurationCollection(typeof(SectionCollection), AddItemName = "tab")]
|
||||
[ConfigurationProperty("", IsDefaultCollection = true)]
|
||||
public TabCollection TabCollection
|
||||
{
|
||||
get { return (TabCollection)base[""]; }
|
||||
set { base[""] = value; }
|
||||
}
|
||||
|
||||
IEnumerable<IDashboardTab> ISection.Tabs
|
||||
{
|
||||
get { return TabCollection; }
|
||||
}
|
||||
|
||||
string ISection.Area
|
||||
{
|
||||
get { return Area.AreaName; }
|
||||
}
|
||||
|
||||
IAccess ISection.AccessRights
|
||||
{
|
||||
get { return Access; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class TabCollection : ConfigurationElementCollection, IEnumerable<IDashboardTab>
|
||||
{
|
||||
internal void Add(TabElement c)
|
||||
{
|
||||
BaseAdd(c);
|
||||
}
|
||||
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new TabElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((TabElement)element).Caption;
|
||||
}
|
||||
|
||||
IEnumerator<IDashboardTab> IEnumerable<IDashboardTab>.GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
yield return BaseGet(i) as IDashboardTab;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class TabElement : ConfigurationElement, IDashboardTab
|
||||
{
|
||||
[ConfigurationProperty("caption", IsRequired = true)]
|
||||
public string Caption
|
||||
{
|
||||
get { return (string)this["caption"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("access")]
|
||||
public AccessElement Access
|
||||
{
|
||||
get { return (AccessElement)this["access"]; }
|
||||
}
|
||||
|
||||
[ConfigurationCollection(typeof(ControlCollection), AddItemName = "control")]
|
||||
[ConfigurationProperty("", IsDefaultCollection = true)]
|
||||
public ControlCollection ControlCollection
|
||||
{
|
||||
get { return (ControlCollection)base[""]; }
|
||||
set { base[""] = value; }
|
||||
}
|
||||
|
||||
IEnumerable<IDashboardControl> IDashboardTab.Controls
|
||||
{
|
||||
get { return ControlCollection; }
|
||||
}
|
||||
|
||||
IAccess IDashboardTab.AccessRights
|
||||
{
|
||||
get { return Access; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,10 +265,10 @@ namespace Umbraco.Core.Configuration
|
||||
/// <param name="value">Value of the setting to be saved.</param>
|
||||
internal static void SaveSetting(string key, string value)
|
||||
{
|
||||
var fileName = GetFullWebConfigFileName();
|
||||
var fileName = IOHelper.MapPath(string.Format("{0}/appSettings.config", SystemDirectories.Config));
|
||||
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
|
||||
var appSettings = xml.Root.Descendants("appSettings").Single();
|
||||
var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single();
|
||||
|
||||
// Update appSetting if it exists, or else create a new appSetting for the given key and value
|
||||
var setting = appSettings.Descendants("add").FirstOrDefault(s => s.Attribute("key").Value == key);
|
||||
@@ -287,10 +287,10 @@ namespace Umbraco.Core.Configuration
|
||||
/// <param name="key">Key of the setting to be removed.</param>
|
||||
internal static void RemoveSetting(string key)
|
||||
{
|
||||
var fileName = GetFullWebConfigFileName();
|
||||
var fileName = IOHelper.MapPath(string.Format("{0}/appSettings.config", SystemDirectories.Config));
|
||||
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
|
||||
var appSettings = xml.Root.Descendants("appSettings").Single();
|
||||
var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single();
|
||||
var setting = appSettings.Descendants("add").FirstOrDefault(s => s.Attribute("key").Value == key);
|
||||
|
||||
if (setting != null)
|
||||
@@ -301,21 +301,6 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetFullWebConfigFileName()
|
||||
{
|
||||
var webConfig = new WebConfigurationFileMap();
|
||||
var vDir = FullpathToRoot;
|
||||
|
||||
foreach (VirtualDirectoryMapping v in webConfig.VirtualDirectories)
|
||||
{
|
||||
if (v.IsAppRoot)
|
||||
vDir = v.PhysicalDirectory;
|
||||
}
|
||||
|
||||
var fileName = System.IO.Path.Combine(vDir, "web.config");
|
||||
return fileName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to root.
|
||||
/// </summary>
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace Umbraco.Core.Configuration
|
||||
//now validate and set the raw value
|
||||
if (RawXml.HasElements)
|
||||
throw new InvalidOperationException("An InnerTextConfigurationElement cannot contain any child elements, only attributes and a value");
|
||||
RawValue = RawXml.Value;
|
||||
|
||||
RawValue = RawXml.Value.Trim();
|
||||
|
||||
//RawValue = reader.ReadElementContentAsString();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Configuration.BaseRest;
|
||||
using Umbraco.Core.Configuration.Dashboard;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -31,7 +32,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// </summary>
|
||||
private UmbracoConfig()
|
||||
{
|
||||
if (UmbracoSettings() == null)
|
||||
if (_umbracoSettings == null)
|
||||
{
|
||||
var umbracoSettings = ConfigurationManager.GetSection("umbracoConfiguration/settings") as IUmbracoSettingsSection;
|
||||
if (umbracoSettings == null)
|
||||
@@ -41,7 +42,7 @@ namespace Umbraco.Core.Configuration
|
||||
SetUmbracoSettings(umbracoSettings);
|
||||
}
|
||||
|
||||
if (BaseRestExtensions() == null)
|
||||
if (_baseRestExtensions == null)
|
||||
{
|
||||
var baseRestExtensions = ConfigurationManager.GetSection("umbracoConfiguration/BaseRestExtensions") as IBaseRestSection;
|
||||
if (baseRestExtensions == null)
|
||||
@@ -50,6 +51,16 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
SetBaseRestExtensions(baseRestExtensions);
|
||||
}
|
||||
|
||||
if (_dashboardSection == null)
|
||||
{
|
||||
var dashboardConfig = ConfigurationManager.GetSection("umbracoConfiguration/dashBoard") as IDashboardSection;
|
||||
if (dashboardConfig == null)
|
||||
{
|
||||
LogHelper.Warn<UmbracoConfig>("Could not load the " + typeof(IDashboardSection) + " from config file!");
|
||||
}
|
||||
SetDashboardSettings(dashboardConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,13 +68,31 @@ namespace Umbraco.Core.Configuration
|
||||
/// </summary>
|
||||
/// <param name="umbracoSettings"></param>
|
||||
/// <param name="baseRestSettings"></param>
|
||||
public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings)
|
||||
/// <param name="dashboardSettings"></param>
|
||||
public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings, IDashboardSection dashboardSettings)
|
||||
{
|
||||
SetUmbracoSettings(umbracoSettings);
|
||||
SetBaseRestExtensions(baseRestSettings);
|
||||
SetDashboardSettings(dashboardSettings);
|
||||
}
|
||||
|
||||
private IDashboardSection _dashboardSection;
|
||||
private IUmbracoSettingsSection _umbracoSettings;
|
||||
private IBaseRestSection _baseRestExtensions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IDashboardSection
|
||||
/// </summary>
|
||||
public IDashboardSection DashboardSettings()
|
||||
{
|
||||
return _dashboardSection;
|
||||
}
|
||||
|
||||
//ONLY for unit testing
|
||||
internal void SetDashboardSettings(IDashboardSection value)
|
||||
{
|
||||
_dashboardSection = value;
|
||||
}
|
||||
|
||||
//ONLY for unit testing
|
||||
internal void SetUmbracoSettings(IUmbracoSettingsSection value)
|
||||
@@ -78,9 +107,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
return _umbracoSettings;
|
||||
}
|
||||
|
||||
private IBaseRestSection _baseRestExtensions;
|
||||
|
||||
|
||||
//ONLY for unit testing
|
||||
public void SetBaseRestExtensions(IBaseRestSection value)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
[ConfigurationKey("umbracoConfiguration/settings/content")]
|
||||
|
||||
internal class ContentElement : ConfigurationElement, IContentSection
|
||||
{
|
||||
[ConfigurationProperty("imaging")]
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
[ConfigurationKey("umbracoConfiguration/settings")]
|
||||
|
||||
public class UmbracoSettingsSection : ConfigurationSection, IUmbracoSettingsSection
|
||||
{
|
||||
[ConfigurationProperty("content")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "alpha"; } }
|
||||
public static string CurrentComment { get { return "beta"; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -53,6 +53,11 @@
|
||||
/// </summary>
|
||||
public const string Content = "content";
|
||||
|
||||
/// <summary>
|
||||
/// alias for the media tree.
|
||||
/// </summary>
|
||||
public const string Members = "member";
|
||||
|
||||
/// <summary>
|
||||
/// alias for the media tree.
|
||||
/// </summary>
|
||||
|
||||
@@ -99,6 +99,10 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class Member
|
||||
{
|
||||
public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider";
|
||||
|
||||
public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for a Members Password Question
|
||||
/// </summary>
|
||||
|
||||
@@ -411,6 +411,11 @@ namespace Umbraco.Core
|
||||
/// Alias for the XPath DropDownList datatype.
|
||||
/// </summary>
|
||||
public const string XPathDropDownListAlias = "Umbraco.XPathDropDownList";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the email address property editor
|
||||
/// </summary>
|
||||
public const string EmailAddressAlias = "Umbraco.EmailAddress";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
@@ -17,6 +18,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Core.Publishing;
|
||||
using Umbraco.Core.Macros;
|
||||
using Umbraco.Core.Services;
|
||||
@@ -308,25 +310,35 @@ namespace Umbraco.Core
|
||||
//the database migration objects
|
||||
MigrationResolver.Current = new MigrationResolver(
|
||||
() => PluginManager.Current.ResolveMigrationTypes());
|
||||
|
||||
//NOTE: These are legacy in v7+ and will eventually need to be removed.
|
||||
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
|
||||
|
||||
// todo: remove once we drop IPropertyEditorValueConverter support.
|
||||
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
|
||||
PluginManager.Current.ResolvePropertyEditorValueConverters());
|
||||
|
||||
//add the internal ones, these are not public currently so need to add them manually
|
||||
PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
|
||||
PluginManager.Current.ResolvePropertyValueConverters());
|
||||
// fixme - why not use the following syntax?
|
||||
//PluginManager.Current.ResolveTypes<IPropertyValueConverter>());
|
||||
|
||||
// this is how we'd switch over to DefaultShortStringHelper _and_ still use
|
||||
// UmbracoSettings UrlReplaceCharacters...
|
||||
//ShortStringHelperResolver.Current = new ShortStringHelperResolver(
|
||||
// new DefaultShortStringHelper().WithConfig(DefaultShortStringHelper.ApplyUrlReplaceCharacters));
|
||||
|
||||
// use the Legacy one for now
|
||||
// use the new DefaultShortStringHelper but sort-of remain compatible
|
||||
// - use UmbracoSettings UrlReplaceCharacters
|
||||
// - allow underscores in terms, allow leading digits
|
||||
ShortStringHelperResolver.Current = new ShortStringHelperResolver(
|
||||
new LegacyShortStringHelper());
|
||||
new DefaultShortStringHelper()
|
||||
.WithConfig(CleanStringType.Url, DefaultShortStringHelper.ApplyUrlReplaceCharacters,
|
||||
allowUnderscoreInTerm: true, allowLeadingDigits: true));
|
||||
|
||||
// that was the old one
|
||||
//ShortStringHelperResolver.Current = new ShortStringHelperResolver(
|
||||
// new LegacyShortStringHelper());
|
||||
|
||||
UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver(
|
||||
typeof (DefaultUrlSegmentProvider));
|
||||
|
||||
// keep it internal for now
|
||||
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
|
||||
// new PublishedContentModelFactoryImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Web.Configuration;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
@@ -28,7 +29,7 @@ namespace Umbraco.Core
|
||||
private string _providerName;
|
||||
private DatabaseSchemaResult _result;
|
||||
|
||||
internal DatabaseContext(IDatabaseFactory factory)
|
||||
public DatabaseContext(IDatabaseFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
@@ -112,7 +113,7 @@ namespace Umbraco.Core
|
||||
public void ConfigureEmbeddedDatabaseConnection()
|
||||
{
|
||||
const string providerName = "System.Data.SqlServerCe.4.0";
|
||||
const string connectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;File Access Retry Timeout=10";
|
||||
const string connectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;";
|
||||
|
||||
SaveConnectionString(connectionString, providerName);
|
||||
|
||||
@@ -248,20 +249,10 @@ namespace Umbraco.Core
|
||||
|
||||
_connectionString = connectionString;
|
||||
_providerName = providerName;
|
||||
|
||||
var webConfig = new WebConfigurationFileMap();
|
||||
var vDir = GlobalSettings.FullpathToRoot;
|
||||
foreach (VirtualDirectoryMapping v in webConfig.VirtualDirectories)
|
||||
{
|
||||
if (v.IsAppRoot)
|
||||
{
|
||||
vDir = v.PhysicalDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
var fileName = Path.Combine(vDir, "web.config");
|
||||
|
||||
var fileName = IOHelper.MapPath(string.Format("{0}/connectionStrings.config", SystemDirectories.Config));
|
||||
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
var connectionstrings = xml.Root.Descendants("connectionStrings").Single();
|
||||
var connectionstrings = xml.Root.DescendantsAndSelf("connectionStrings").Single();
|
||||
|
||||
// Update connectionString if it exists, or else create a new appSetting for the given key and value
|
||||
var setting = connectionstrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == GlobalSettings.UmbracoConnectionName);
|
||||
|
||||
@@ -112,7 +112,8 @@ namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
//don't log here, we return this exception because the caller may need to do something specific when
|
||||
//this exception occurs.
|
||||
return Attempt<TryInvokeMemberResult>.Fail(ext);
|
||||
var mresult = new TryInvokeMemberResult(null, TryInvokeMemberSuccessReason.FoundExtensionMethod);
|
||||
return Attempt<TryInvokeMemberResult>.Fail(mresult, ext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -124,7 +125,8 @@ namespace Umbraco.Core.Dynamics
|
||||
sb.Append(t + ",");
|
||||
}
|
||||
LogHelper.Error<DynamicInstanceHelper>(sb.ToString(), ex);
|
||||
return Attempt<TryInvokeMemberResult>.Fail(ex);
|
||||
var mresult = new TryInvokeMemberResult(null, TryInvokeMemberSuccessReason.FoundExtensionMethod);
|
||||
return Attempt<TryInvokeMemberResult>.Fail(mresult, ex);
|
||||
}
|
||||
}
|
||||
return Attempt<TryInvokeMemberResult>.Fail();
|
||||
|
||||
@@ -11,78 +11,101 @@ namespace Umbraco.Core.Dynamics
|
||||
//Because it's IEnumerable, if the user is actually trying @Model.TextPages or similar
|
||||
//it will still return an enumerable object (assuming the call actually failed because there were no children of that type)
|
||||
//but in .Where, if they use a property that doesn't exist, the lambda will bypass this and return false
|
||||
|
||||
// returned when TryGetMember fails on a DynamicPublishedContent
|
||||
//
|
||||
// so if user does @CurrentPage.TextPages it will get something that is enumerable (but empty)
|
||||
// note - not sure I understand the stuff about .Where, though
|
||||
|
||||
public class DynamicNull : DynamicObject, IEnumerable, IHtmlString
|
||||
{
|
||||
public static readonly DynamicNull Null = new DynamicNull();
|
||||
|
||||
private DynamicNull() {}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return (new List<DynamicNull>()).GetEnumerator();
|
||||
}
|
||||
|
||||
public DynamicNull Where(string predicate, params object[] values)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicNull OrderBy(string orderBy)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicNull ToContentSet()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public int Count()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
{
|
||||
result = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
|
||||
{
|
||||
result = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
|
||||
{
|
||||
result = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsNull()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HasValue()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public static implicit operator bool(DynamicNull n)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static implicit operator DateTime(DynamicNull n)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
public static implicit operator int(DynamicNull n)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static implicit operator string(DynamicNull n)
|
||||
{
|
||||
return string.Empty;
|
||||
@@ -92,6 +115,5 @@ namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace Umbraco.Core.Dynamics
|
||||
if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod
|
||||
&& attempt.Exception != null && attempt.Exception is TargetInvocationException)
|
||||
{
|
||||
result = new DynamicNull();
|
||||
result = DynamicNull.Null;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,96 @@ namespace Umbraco.Core.Dynamics
|
||||
/// Utility class for finding extension methods on a type to execute
|
||||
/// </summary>
|
||||
internal static class ExtensionMethodFinder
|
||||
{
|
||||
{
|
||||
private static readonly MethodInfo[] AllExtensionMethods;
|
||||
|
||||
static ExtensionMethodFinder()
|
||||
{
|
||||
AllExtensionMethods = TypeFinder.GetAssembliesWithKnownExclusions()
|
||||
// assemblies that contain extension methods
|
||||
.Where(a => a.IsDefined(typeof(ExtensionAttribute), false))
|
||||
// types that contain extension methods
|
||||
.SelectMany(a => a.GetTypes()
|
||||
.Where(t => t.IsDefined(typeof(ExtensionAttribute), false) && t.IsSealed && t.IsGenericType == false && t.IsNested == false))
|
||||
// actual extension methods
|
||||
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
.Where(m => m.IsDefined(typeof(ExtensionAttribute), false)))
|
||||
// and also IEnumerable<T> extension methods - because the assembly is excluded
|
||||
.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// ORIGINAL CODE IS NOT COMPLETE, DOES NOT HANDLE GENERICS, ETC...
|
||||
|
||||
// so this is an attempt at fixing things, but it's not done yet
|
||||
// and do we really want to do this? extension methods are not supported on dynamics, period
|
||||
// we should use strongly typed content instead of dynamics.
|
||||
|
||||
/*
|
||||
|
||||
// get all extension methods for type thisType, with name name,
|
||||
// accepting argsCount arguments (not counting the instance of thisType).
|
||||
private static IEnumerable<MethodInfo> GetExtensionMethods(Type thisType, string name, int argsCount)
|
||||
{
|
||||
var key = string.Format("{0}.{1}::{2}", thisType.FullName, name, argsCount);
|
||||
|
||||
var types = thisType.GetBaseTypes(true); // either do this OR have MatchFirstParameter handle the stuff... F*XME
|
||||
|
||||
var methods = AllExtensionMethods
|
||||
.Where(m => m.Name == name)
|
||||
.Where(m => m.GetParameters().Length == argsCount)
|
||||
.Where(m => MatchFirstParameter(thisType, m.GetParameters()[0].ParameterType));
|
||||
|
||||
// f*xme - is this what we should cache?
|
||||
return methods;
|
||||
}
|
||||
|
||||
// find out whether the first parameter is a match for thisType
|
||||
private static bool MatchFirstParameter(Type thisType, Type firstParameterType)
|
||||
{
|
||||
return MethodArgZeroHasCorrectTargetType(null, firstParameterType, thisType);
|
||||
}
|
||||
|
||||
// get the single extension method for type thisType, with name name,
|
||||
// that accepts the arguments in args (which does not contain the instance of thisType).
|
||||
public static MethodInfo GetExtensionMethod(Type thisType, string name, object[] args)
|
||||
{
|
||||
MethodInfo result = null;
|
||||
foreach (var method in GetExtensionMethods(thisType, name, args.Length).Where(m => MatchParameters(m, args)))
|
||||
{
|
||||
if (result == null)
|
||||
result = method;
|
||||
else
|
||||
throw new AmbiguousMatchException("More than one matching extension method was found.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// find out whether the method can accept the arguments
|
||||
private static bool MatchParameters(MethodInfo method, IList<object> args)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
var i = 0;
|
||||
for (; i < parameters.Length; ++i)
|
||||
{
|
||||
if (MatchParameter(parameters[i].ParameterType, args[i].GetType()) == false)
|
||||
break;
|
||||
}
|
||||
return (i == parameters.Length);
|
||||
}
|
||||
|
||||
internal static bool MatchParameter(Type parameterType, Type argumentType)
|
||||
{
|
||||
// public static int DoSomething<T>(Foo foo, T t1, T t2)
|
||||
// DoSomething(foo, t1, t2) => how can we match?!
|
||||
return parameterType == argumentType; // f*xme of course!
|
||||
}
|
||||
*
|
||||
*/
|
||||
|
||||
// BELOW IS THE ORIGINAL CODE...
|
||||
|
||||
/// <summary>
|
||||
/// Returns all extension methods found matching the definition
|
||||
/// </summary>
|
||||
@@ -27,6 +116,10 @@ namespace Umbraco.Core.Dynamics
|
||||
/// </remarks>
|
||||
private static IEnumerable<MethodInfo> GetAllExtensionMethods(Type thisType, string name, int argumentCount, bool argsContainsThis)
|
||||
{
|
||||
// at *least* we can cache the extension methods discovery
|
||||
var candidates = AllExtensionMethods;
|
||||
|
||||
/*
|
||||
//only scan assemblies we know to contain extension methods (user assemblies)
|
||||
var assembliesToScan = TypeFinder.GetAssembliesWithKnownExclusions();
|
||||
|
||||
@@ -45,6 +138,7 @@ namespace Umbraco.Core.Dynamics
|
||||
|
||||
//add the extension methods defined in IEnumerable
|
||||
candidates = candidates.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public));
|
||||
*/
|
||||
|
||||
//filter by name
|
||||
var methodsByName = candidates.Where(m => m.Name == name);
|
||||
|
||||
@@ -1,60 +1,46 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.interfaces;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
internal class PropertyResult : IPublishedContentProperty, IHtmlString
|
||||
{
|
||||
internal PropertyResult(IPublishedContentProperty source, PropertyResultType type)
|
||||
internal class PropertyResult : IPublishedProperty, IHtmlString
|
||||
{
|
||||
private readonly IPublishedProperty _source;
|
||||
private readonly string _alias;
|
||||
private readonly object _value;
|
||||
private readonly PropertyResultType _type;
|
||||
|
||||
internal PropertyResult(IPublishedProperty source, PropertyResultType type)
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException("source");
|
||||
|
||||
Alias = source.Alias;
|
||||
Value = source.Value;
|
||||
PropertyType = type;
|
||||
|
||||
_type = type;
|
||||
_source = source;
|
||||
}
|
||||
|
||||
internal PropertyResult(string alias, object value, PropertyResultType type)
|
||||
{
|
||||
if (alias == null) throw new ArgumentNullException("alias");
|
||||
if (value == null) throw new ArgumentNullException("value");
|
||||
|
||||
Alias = alias;
|
||||
Value = value;
|
||||
PropertyType = type;
|
||||
_type = type;
|
||||
_alias = alias;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
internal PropertyResultType PropertyType { get; private set; }
|
||||
|
||||
public string Alias { get; private set; }
|
||||
internal PropertyResultType PropertyType { get { return _type; } }
|
||||
|
||||
public object Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value as a string output, this is used in the final rendering process of a property
|
||||
/// </summary>
|
||||
internal string ValueAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return Value == null ? "" : Convert.ToString(Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Id of the document for which this property belongs to
|
||||
/// </summary>
|
||||
public int DocumentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The alias of the document type alias for which this property belongs to
|
||||
/// </summary>
|
||||
public string DocumentTypeAlias { get; set; }
|
||||
public string PropertyTypeAlias { get { return _source == null ? _alias : _source.PropertyTypeAlias; } }
|
||||
public object DataValue { get { return _source == null ? _value : _source.DataValue; } }
|
||||
public bool HasValue { get { return _source == null || _source.HasValue; } }
|
||||
public object Value { get { return _source == null ? _value : _source.Value; } }
|
||||
public object XPathValue { get { return Value == null ? null : Value.ToString(); } }
|
||||
|
||||
public string ToHtmlString()
|
||||
{
|
||||
return ValueAsString;
|
||||
var value = Value;
|
||||
return value == null ? string.Empty : value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,16 +13,45 @@ namespace Umbraco.Core
|
||||
{
|
||||
public static IEnumerable<IEnumerable<T>> InGroupsOf<T>(this IEnumerable<T> source, int groupSize)
|
||||
{
|
||||
var i = 0;
|
||||
var length = source.Count();
|
||||
if (source == null)
|
||||
throw new NullReferenceException("source");
|
||||
|
||||
while ((i * groupSize) < length)
|
||||
{
|
||||
yield return source.Skip(i * groupSize).Take(groupSize);
|
||||
i++;
|
||||
}
|
||||
// enumerate the source only once!
|
||||
return new InGroupsEnumerator<T>(source, groupSize).Groups();
|
||||
}
|
||||
|
||||
// this class makes sure that the source is enumerated only ONCE
|
||||
// which means that when it is enumerated, the actual groups content
|
||||
// has to be evaluated at the same time, and stored in an array.
|
||||
private class InGroupsEnumerator<T>
|
||||
{
|
||||
private readonly IEnumerator<T> _source;
|
||||
private readonly int _count;
|
||||
private bool _mightHaveNext;
|
||||
|
||||
public InGroupsEnumerator(IEnumerable<T> source, int count)
|
||||
{
|
||||
_source = source.GetEnumerator();
|
||||
_count = count;
|
||||
_mightHaveNext = true;
|
||||
}
|
||||
|
||||
public IEnumerable<IEnumerable<T>> Groups()
|
||||
{
|
||||
while (_mightHaveNext && _source.MoveNext())
|
||||
yield return Group().ToArray(); // see note above
|
||||
}
|
||||
|
||||
private IEnumerable<T> Group()
|
||||
{
|
||||
var c = 0;
|
||||
do
|
||||
{
|
||||
yield return _source.Current;
|
||||
} while (++c < _count && _source.MoveNext());
|
||||
_mightHaveNext = c == _count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The distinct by.</summary>
|
||||
/// <param name="source">The source.</param>
|
||||
@@ -243,23 +272,45 @@ namespace Umbraco.Core
|
||||
});
|
||||
}
|
||||
|
||||
///<summary>Finds the index of the first item matching an expression in an enumerable.</summary>
|
||||
///<param name="items">The enumerable to search.</param>
|
||||
///<param name="predicate">The expression to test the items against.</param>
|
||||
///<returns>The index of the first matching item, or -1 if no items match.</returns>
|
||||
/// <summary>
|
||||
/// Finds the index of the first item matching an expression in an enumerable.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the enumerated objects.</typeparam>
|
||||
/// <param name="items">The enumerable to search.</param>
|
||||
/// <param name="predicate">The expression to test the items against.</param>
|
||||
/// <returns>The index of the first matching item, or -1.</returns>
|
||||
public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate)
|
||||
{
|
||||
return FindIndex(items, 0, predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the index of the first item matching an expression in an enumerable.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the enumerated objects.</typeparam>
|
||||
/// <param name="items">The enumerable to search.</param>
|
||||
/// <param name="startIndex">The index to start at.</param>
|
||||
/// <param name="predicate">The expression to test the items against.</param>
|
||||
/// <returns>The index of the first matching item, or -1.</returns>
|
||||
public static int FindIndex<T>(this IEnumerable<T> items, int startIndex, Func<T, bool> predicate)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException("items");
|
||||
if (predicate == null) throw new ArgumentNullException("predicate");
|
||||
if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex");
|
||||
|
||||
var index = startIndex;
|
||||
if (index > 0)
|
||||
items = items.Skip(index);
|
||||
|
||||
var retVal = 0;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (predicate(item)) return retVal;
|
||||
retVal++;
|
||||
if (predicate(item)) return index;
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
///<summary>Finds the index of the first occurence of an item in an enumerable.</summary>
|
||||
///<param name="items">The enumerable to search.</param>
|
||||
///<param name="item">The item to find.</param>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Any class implementing this interface that is added to the httpcontext.items keys or values will be disposed of at the end of the request.
|
||||
/// </summary>
|
||||
public interface IDisposeOnRequestEnd : IDisposable
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Macros
|
||||
/// </summary>
|
||||
internal class MacroTagParser
|
||||
{
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<div class=[""']umb-macro-holder.+?[""'].*?>.*?<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(.*?</div>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<div.*?>.*?<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(.*?</div>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""'](\w+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
@@ -18,7 +19,8 @@ namespace Umbraco.Core.Manifest
|
||||
private readonly DirectoryInfo _pluginsDir;
|
||||
|
||||
//used to strip comments
|
||||
private static readonly Regex Comments = new Regex("(/\\*.*\\*/)", RegexOptions.Compiled);
|
||||
private static readonly Regex CommentsSurround = new Regex(@"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/", RegexOptions.Compiled);
|
||||
private static readonly Regex CommentsLine = new Regex(@"//.*?$", RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
|
||||
public ManifestParser(DirectoryInfo pluginsDir)
|
||||
{
|
||||
@@ -116,11 +118,20 @@ namespace Umbraco.Core.Manifest
|
||||
{
|
||||
if (m.IsNullOrWhiteSpace()) continue;
|
||||
|
||||
//remove any comments first, NOTE: I think JSON.Net will do this for us! but we'll leave it here for now
|
||||
Comments.Replace(m, match => "");
|
||||
//remove any comments first
|
||||
var replaced = CommentsSurround.Replace(m, match => " ");
|
||||
replaced = CommentsLine.Replace(replaced, match => "");
|
||||
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<JObject>(m);
|
||||
JObject deserialized;
|
||||
try
|
||||
{
|
||||
deserialized = JsonConvert.DeserializeObject<JObject>(replaced);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<ManifestParser>("An error occurred parsing manifest with contents: " + m, ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
//validate the javascript
|
||||
var init = deserialized.Properties().Where(x => x.Name == "javascript").ToArray();
|
||||
@@ -128,7 +139,14 @@ namespace Umbraco.Core.Manifest
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'javascript' element");
|
||||
}
|
||||
|
||||
|
||||
//validate the css
|
||||
var cssinit = deserialized.Properties().Where(x => x.Name == "css").ToArray();
|
||||
if (cssinit.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'css' element");
|
||||
}
|
||||
|
||||
//validate the property editors section
|
||||
var propEditors = deserialized.Properties().Where(x => x.Name == "propertyEditors").ToArray();
|
||||
if (propEditors.Length > 1)
|
||||
@@ -139,6 +157,9 @@ namespace Umbraco.Core.Manifest
|
||||
var jConfig = init.Any() ? (JArray)deserialized["javascript"] : new JArray();
|
||||
ReplaceVirtualPaths(jConfig);
|
||||
|
||||
var cssConfig = cssinit.Any() ? (JArray)deserialized["css"] : new JArray();
|
||||
ReplaceVirtualPaths(cssConfig);
|
||||
|
||||
//replace virtual paths for each property editor
|
||||
if (deserialized["propertyEditors"] != null)
|
||||
{
|
||||
@@ -158,6 +179,7 @@ namespace Umbraco.Core.Manifest
|
||||
var manifest = new PackageManifest()
|
||||
{
|
||||
JavaScriptInitialize = jConfig,
|
||||
StylesheetInitialize = cssConfig,
|
||||
PropertyEditors = propEditors.Any() ? (JArray)deserialized["propertyEditors"] : new JArray(),
|
||||
ParameterEditors = propEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray()
|
||||
};
|
||||
|
||||
@@ -12,6 +12,11 @@ namespace Umbraco.Core.Manifest
|
||||
/// </summary>
|
||||
public JArray JavaScriptInitialize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The json array used to initialize the application with the CSS dependencies required
|
||||
/// </summary>
|
||||
public JArray StylesheetInitialize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The json array of property editors
|
||||
/// </summary>
|
||||
|
||||
@@ -16,8 +16,15 @@ namespace Umbraco.Core.Media
|
||||
/// <summary>
|
||||
/// A helper class used for imaging
|
||||
/// </summary>
|
||||
internal class ImageHelper
|
||||
internal static class ImageHelper
|
||||
{
|
||||
public static string GetMimeType(this Image image)
|
||||
{
|
||||
var format = image.RawFormat;
|
||||
var codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid);
|
||||
return codec.MimeType;
|
||||
}
|
||||
|
||||
internal static IEnumerable<ResizedImage> GenerateMediaThumbnails(
|
||||
IFileSystem fs,
|
||||
string fileName,
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Diagnostics;
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
[DebuggerDisplay("Tree - {Title} ({ApplicationAlias})")]
|
||||
internal class ApplicationTree
|
||||
public class ApplicationTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationTree"/> class.
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="iconClosed">The icon closed.</param>
|
||||
/// <param name="iconOpened">The icon opened.</param>
|
||||
/// <param name="type">The tree type.</param>
|
||||
public ApplicationTree(bool initialize, byte sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
|
||||
public ApplicationTree(bool initialize, int sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
|
||||
{
|
||||
this.Initialize = initialize;
|
||||
this.SortOrder = sortOrder;
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Core.Models
|
||||
/// Gets or sets the sort order.
|
||||
/// </summary>
|
||||
/// <value>The sort order.</value>
|
||||
public byte SortOrder { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application alias.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -50,6 +51,7 @@ namespace Umbraco.Core.Models
|
||||
_contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture));
|
||||
_properties = properties;
|
||||
_properties.EnsurePropertyTypes(PropertyTypes);
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,6 +75,7 @@ namespace Umbraco.Core.Models
|
||||
_contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture));
|
||||
_properties = properties;
|
||||
_properties.EnsurePropertyTypes(PropertyTypes);
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<ContentBase, string>(x => x.Name);
|
||||
@@ -253,6 +256,21 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IDictionary<string, object> IUmbracoEntity.AdditionalData
|
||||
{
|
||||
get { return _additionalData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of PropertyGroups available on this Content object
|
||||
/// </summary>
|
||||
@@ -446,7 +464,7 @@ namespace Umbraco.Core.Models
|
||||
/// also reset the dirty changes made to the content's Properties (user defined)
|
||||
/// </summary>
|
||||
/// <param name="rememberPreviouslyChangedProperties"></param>
|
||||
internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Umbraco.Core.Models
|
||||
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)
|
||||
@@ -71,9 +71,9 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
//check if any user prop has changed
|
||||
var propertyValueChanged = ((Content) entity).IsAnyUserPropertyDirty();
|
||||
var propertyValueChanged = ((Content)entity).IsAnyUserPropertyDirty();
|
||||
//check if any content prop has changed
|
||||
var contentDataChanged = ((Content) entity).IsEntityDirty();
|
||||
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;
|
||||
@@ -296,7 +296,7 @@ namespace Umbraco.Core.Models
|
||||
.Select(propertyType => propertyType.Id)
|
||||
.Contains(property.PropertyTypeId))
|
||||
.OrderBy(x => x.PropertyType.SortOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set property values by alias with an annonymous object
|
||||
@@ -334,6 +334,8 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
#region SetValue for setting file contents
|
||||
|
||||
/// <summary>
|
||||
/// Sets and uploads the file from a HttpPostedFileBase object as the property value
|
||||
/// </summary>
|
||||
@@ -380,7 +382,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
SetValue(content, propertyTypeAlias, (HttpPostedFileBase)value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets and uploads the file from a <see cref="Stream"/> as the property value
|
||||
/// </summary>
|
||||
@@ -416,7 +418,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
fs.AddFile(fileName, fileStream);
|
||||
|
||||
|
||||
//Check if file supports resizing and create thumbnails
|
||||
var supportsResizing = UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.InvariantContains(extension);
|
||||
|
||||
@@ -461,14 +463,14 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
ImageHelper.GenerateMediaThumbnails(fs, fileName, extension, originalImage, additionalSizes);
|
||||
|
||||
|
||||
//while the image is still open, we'll check if we need to auto-populate the image properties
|
||||
if (uploadFieldConfigNode != null)
|
||||
{
|
||||
content.SetValue(uploadFieldConfigNode.WidthFieldAlias, originalImage.Width.ToString(CultureInfo.InvariantCulture));
|
||||
content.SetValue(uploadFieldConfigNode.HeightFieldAlias, originalImage.Height.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,12 +484,15 @@ namespace Umbraco.Core.Models
|
||||
//Set the value of the property to that of the uploaded file's url
|
||||
property.Value = fs.GetUrl(fileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Creator of this media item.
|
||||
/// </summary>
|
||||
public static IProfile GetCreatorProfile(this IMedia media)
|
||||
{
|
||||
|
||||
#endregion
|
||||
|
||||
#region User/Profile methods
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IProfile"/> for the Creator of this media item.
|
||||
/// </summary>
|
||||
public static IProfile GetCreatorProfile(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.UserService.GetProfileById(media.CreatorId);
|
||||
}
|
||||
|
||||
@@ -506,6 +511,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
return ApplicationContext.Current.Services.UserService.GetProfileById(content.WriterId);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an <see cref="IContent"/> item has any published versions
|
||||
@@ -519,7 +525,77 @@ namespace Umbraco.Core.Models
|
||||
|
||||
return ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
|
||||
}
|
||||
|
||||
#region Tag methods
|
||||
|
||||
/// <summary>
|
||||
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item to assign the tags to</param>
|
||||
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
|
||||
/// <param name="tags">The tags to assign</param>
|
||||
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
|
||||
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
|
||||
/// <returns></returns>
|
||||
public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
var property = content.Properties[propertyTypeAlias];
|
||||
if (property == null)
|
||||
{
|
||||
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
|
||||
}
|
||||
|
||||
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
|
||||
|
||||
property.TagSupport.Enable = true;
|
||||
property.TagSupport.Tags = trimmedTags.Select(x => new Tuple<string, string>(x, tagGroup));
|
||||
property.TagSupport.Behavior = replaceTags ? PropertyTagBehavior.Replace : PropertyTagBehavior.Merge;
|
||||
|
||||
//ensure the property value is set to the same thing
|
||||
if (replaceTags)
|
||||
{
|
||||
property.Value = string.Join(",", trimmedTags);
|
||||
}
|
||||
else
|
||||
{
|
||||
var currTags = property.Value.ToString().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim());
|
||||
property.Value = string.Join(",", trimmedTags.Union(currTags));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove any of the tags specified in the collection from the property if they are currently assigned.
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="propertyTypeAlias"></param>
|
||||
/// <param name="tags"></param>
|
||||
/// <param name="tagGroup">The group/category that the tags are currently assigned to, the default value is "default"</param>
|
||||
public static void RemoveTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, string tagGroup = "default")
|
||||
{
|
||||
var property = content.Properties[propertyTypeAlias];
|
||||
if (property == null)
|
||||
{
|
||||
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
|
||||
}
|
||||
|
||||
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
|
||||
|
||||
property.TagSupport.Behavior = PropertyTagBehavior.Remove;
|
||||
property.TagSupport.Enable = true;
|
||||
property.TagSupport.Tags = trimmedTags.Select(x => new Tuple<string, string>(x, tagGroup));
|
||||
|
||||
//set the property value
|
||||
var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim());
|
||||
|
||||
property.Value = string.Join(",", currTags.Except(trimmedTags));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region XML methods
|
||||
/// <summary>
|
||||
/// Creates the full xml representation for the <see cref="IContent"/> object and all of it's descendants
|
||||
/// </summary>
|
||||
@@ -567,5 +643,10 @@ namespace Umbraco.Core.Models
|
||||
//If current IContent is published we should get latest unpublished version
|
||||
return content.ToXml();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -42,6 +43,7 @@ namespace Umbraco.Core.Models
|
||||
_allowedContentTypes = new List<ContentTypeSort>();
|
||||
_propertyGroups = new PropertyGroupCollection();
|
||||
_propertyTypes = new PropertyTypeCollection();
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
protected ContentTypeBase(IContentTypeBase parent)
|
||||
@@ -52,6 +54,7 @@ namespace Umbraco.Core.Models
|
||||
_allowedContentTypes = new List<ContentTypeSort>();
|
||||
_propertyGroups = new PropertyGroupCollection();
|
||||
_propertyTypes = new PropertyTypeCollection();
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, string>(x => x.Name);
|
||||
@@ -314,6 +317,21 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IDictionary<string, object> IUmbracoEntity.AdditionalData
|
||||
{
|
||||
get { return _additionalData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of integer Ids for allowed ContentTypes
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -33,12 +35,15 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_parentId = parentId;
|
||||
_propertyEditorAlias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(controlId, true);
|
||||
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public DataTypeDefinition(int parentId, string propertyEditorAlias)
|
||||
{
|
||||
_parentId = parentId;
|
||||
_propertyEditorAlias = propertyEditorAlias;
|
||||
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, string>(x => x.Name);
|
||||
@@ -170,9 +175,11 @@ namespace Umbraco.Core.Models
|
||||
_trashed = value;
|
||||
return _trashed;
|
||||
}, _trashed, TrashedSelector);
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["Trashed"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[DataMember]
|
||||
public string PropertyEditorAlias
|
||||
{
|
||||
@@ -184,6 +191,8 @@ namespace Umbraco.Core.Models
|
||||
_propertyEditorAlias = value;
|
||||
return _propertyEditorAlias;
|
||||
}, _propertyEditorAlias, PropertyEditorAliasSelector);
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["DatabaseType"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +212,8 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(value, true);
|
||||
PropertyEditorAlias = alias;
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["ControlId"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,9 +231,27 @@ namespace Umbraco.Core.Models
|
||||
_databaseType = value;
|
||||
return _databaseType;
|
||||
}, _databaseType, DatabaseTypeSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["DatabaseType"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IDictionary<string, object> IUmbracoEntity.AdditionalData
|
||||
{
|
||||
get { return _additionalData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
internal override void AddingEntity()
|
||||
{
|
||||
base.AddingEntity();
|
||||
@@ -231,4 +260,4 @@ namespace Umbraco.Core.Models
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
bool WasDirty();
|
||||
bool WasPropertyDirty(string propertyName);
|
||||
void ForgetPreviouslyDirtyProperties();
|
||||
void ResetDirtyProperties(bool rememberPreviouslyChangedProperties);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
public interface IUmbracoEntity : IAggregateRoot
|
||||
{
|
||||
@@ -41,5 +43,10 @@
|
||||
/// Not all entities support being trashed, they'll always return false.
|
||||
/// </remarks>
|
||||
bool Trashed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
IDictionary<string, object> AdditionalData { get; }
|
||||
}
|
||||
}
|
||||
@@ -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 virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
public virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
if (rememberPreviouslyChangedProperties)
|
||||
{
|
||||
|
||||
@@ -1,20 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a published item in Umbraco
|
||||
/// Represents a cached content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A replacement for INode which needs to occur since INode doesn't contain the document type alias
|
||||
/// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable)
|
||||
/// <para>SD: A replacement for INode which needs to occur since INode doesn't contain the document type alias
|
||||
/// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable).</para>
|
||||
/// <para>Stephan: initially, that was for cached published content only. Now, we're using it also for
|
||||
/// cached preview (so, maybe unpublished) content. A better name would therefore be ICachedContent, as
|
||||
/// has been suggested. However, can't change now. Maybe in v7?</para>
|
||||
/// </remarks>
|
||||
public interface IPublishedContent
|
||||
{
|
||||
int Id { get; }
|
||||
public interface IPublishedContent
|
||||
{
|
||||
#region ContentSet
|
||||
|
||||
// Because of http://issues.umbraco.org/issue/U4-1797 and in order to implement
|
||||
// Index() and methods that derive from it such as IsFirst(), IsLast(), etc... all
|
||||
// content items must know about their containing content set.
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content set to which the content belongs.
|
||||
/// </summary>
|
||||
/// <remarks>The default set consists in the siblings of the content (including the content
|
||||
/// itself) ordered by <c>sortOrder</c>.</remarks>
|
||||
IEnumerable<IPublishedContent> ContentSet { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentType
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type.
|
||||
/// </summary>
|
||||
PublishedContentType ContentType { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content
|
||||
|
||||
int Id { get; }
|
||||
int TemplateId { get; }
|
||||
int SortOrder { get; }
|
||||
string Name { get; }
|
||||
@@ -31,32 +59,97 @@ namespace Umbraco.Core.Models
|
||||
Guid Version { get; }
|
||||
int Level { get; }
|
||||
string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is a content (aka a document) or a media.
|
||||
/// </summary>
|
||||
PublishedItemType ItemType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is draft.
|
||||
/// </summary>
|
||||
/// <remarks>A content is draft when it is the unpublished version of a content, which may
|
||||
/// have a published version, or not.</remarks>
|
||||
bool IsDraft { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the published content within its current owning content set.
|
||||
/// </summary>
|
||||
/// <returns>The index of the published content within its current owning content set.</returns>
|
||||
int GetIndex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tree
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of the content.
|
||||
/// </summary>
|
||||
/// <remarks>The parent of root content is <c>null</c>.</remarks>
|
||||
IPublishedContent Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content.
|
||||
/// </summary>
|
||||
/// <remarks>Children are sorted by their sortOrder.</remarks>
|
||||
IEnumerable<IPublishedContent> Children { get; }
|
||||
|
||||
ICollection<IPublishedContentProperty> Properties { get; }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property value for the property alias specified
|
||||
/// </summary>
|
||||
/// <param name="propertyAlias"></param>
|
||||
/// <returns></returns>
|
||||
object this[string propertyAlias] { get; }
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns a property on the object based on an alias
|
||||
/// <summary>
|
||||
/// Gets the properties of the content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Contains one <c>IPublishedProperty</c> for each property defined for the content type, including
|
||||
/// inherited properties. Some properties may have no value.</para>
|
||||
/// <para>The properties collection of an IPublishedContent instance should be read-only ie it is illegal
|
||||
/// to add properties to the collection.</para>
|
||||
/// </remarks>
|
||||
ICollection<IPublishedProperty> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <returns>The property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If the content type has no property with that alias, including inherited properties, returns <c>null</c>,</para>
|
||||
/// <para>otherwise return a property -- that may have no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <param name="recurse">A value indicating whether to navigate the tree upwards until a property with a value is found.</param>
|
||||
/// <returns>The property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Navigate the tree upwards and look for a property with that alias and with a value (ie <c>HasValue</c> is <c>true</c>).
|
||||
/// If found, return the property. If no property with that alias is found, having a value or not, return <c>null</c>. Otherwise
|
||||
/// return the first property that was found with the alias but had no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias, bool recurse);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Although we do have a a property to return Properties of the object, in some cases a custom implementation may not know
|
||||
/// about all properties until specifically asked for one by alias.
|
||||
///
|
||||
/// This method is mostly used in places such as DynamicPublishedContent when trying to resolve a property based on an alias.
|
||||
/// In some cases Pulish Stores, a property value may exist in multiple places and we need to fallback to different cached locations
|
||||
/// therefore sometimes the 'Properties' collection may not be sufficient.
|
||||
/// </remarks>
|
||||
IPublishedContentProperty GetProperty(string alias);
|
||||
}
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <returns>The value of the property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If <c>GetProperty(alias)</c> is <c>null</c> then returns <c>null</c> else return <c>GetProperty(alias).Value</c>.</para>
|
||||
/// <para>So if the property has no value, returns the default value for that property type.</para>
|
||||
/// <para>This one is defined here really because we cannot define index extension methods, but all it should do is:
|
||||
/// <code>var p = GetProperty(alias); return p == null ? null : p.Value;</code> and nothing else.</para>
|
||||
/// <para>The recursive syntax (eg "_title") is _not_ supported here.</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
object this[string alias] { get; } // todo - should obsolete this[alias] (when?)
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IPublishedContentProperty
|
||||
{
|
||||
string Alias { get; }
|
||||
object Value { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property of an <c>IPublishedContent</c>.
|
||||
/// </summary>
|
||||
public interface IPublishedProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the alias of the property.
|
||||
/// </summary>
|
||||
string PropertyTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the property has a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers
|
||||
/// a missing value.</para>
|
||||
/// <para>The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and
|
||||
/// that includes whitespace-only) strings as "no value".</para>
|
||||
/// <para>Other caches that get their raw value from the database would consider that a property has "no
|
||||
/// value" if it is missing, null, or an empty string (including whitespace-only).</para>
|
||||
/// </remarks>
|
||||
bool HasValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The data value is whatever was passed to the property when it was instanciated, and it is
|
||||
/// somewhat implementation-dependent -- depending on how the IPublishedCache is implemented.</para>
|
||||
/// <para>The XmlPublishedCache raw values are strings exclusively since they come from the Xml cache.</para>
|
||||
/// <para>For other caches that get their raw value from the database, it would be either a string,
|
||||
/// an integer (Int32), or a date and time (DateTime).</para>
|
||||
/// <para>If you're using that value, you're probably wrong, unless you're doing some internal
|
||||
/// Umbraco stuff.</para>
|
||||
/// </remarks>
|
||||
object DataValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The value is what you want to use when rendering content in an MVC view ie in C#.</para>
|
||||
/// <para>It can be null, or any type of CLR object.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XPath value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine.</para>
|
||||
/// <para>It must be either null, or a string, or an XPathNavigator.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object XPathValue { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface ITag : IAggregateRoot
|
||||
{
|
||||
[DataMember]
|
||||
string Text { get; set; }
|
||||
|
||||
[DataMember]
|
||||
string Group { get; set; }
|
||||
|
||||
//TODO: enable this at some stage
|
||||
//int ParentId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,7 @@ namespace Umbraco.Core.Models
|
||||
OnPropertyChanged(PropertiesSelector);
|
||||
}
|
||||
|
||||
internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
_addedProperties.Clear();
|
||||
_removedProperties.Clear();
|
||||
|
||||
@@ -20,19 +20,37 @@ namespace Umbraco.Core.Models
|
||||
private object _providerUserKey;
|
||||
private Type _userTypeKey;
|
||||
|
||||
public Member(string name, int parentId, IMemberType contentType, PropertyCollection properties) : base(name, parentId, contentType, properties)
|
||||
/// <summary>
|
||||
/// Constructor for creating a Member object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
public Member(string name, IMemberType contentType)
|
||||
: base(name, -1, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
public Member(string name, IContentBase parent, IMemberType contentType, PropertyCollection properties)
|
||||
: base(name, parent, contentType, properties)
|
||||
public Member(string name, string email, string username, string password, int parentId, IMemberType contentType)
|
||||
: base(name, parentId, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentType = contentType;
|
||||
_email = email;
|
||||
_username = username;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType)
|
||||
: base(name, parent, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentType = contentType;
|
||||
_email = email;
|
||||
_username = username;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.ContentTypeAlias);
|
||||
@@ -420,7 +438,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public override void ChangeTrashedState(bool isTrashed, int parentId = -20)
|
||||
{
|
||||
throw new NotImplementedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid");
|
||||
throw new NotSupportedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid");
|
||||
}
|
||||
|
||||
/* Internal experiment - only used for mapping queries.
|
||||
|
||||
@@ -114,5 +114,26 @@ namespace Umbraco.Core.Models
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
/// <remarks>Created date is set and a Unique key is assigned</remarks>
|
||||
internal override void AddingEntity()
|
||||
{
|
||||
base.AddingEntity();
|
||||
|
||||
if (Key == Guid.Empty)
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being updated
|
||||
/// </summary>
|
||||
/// <remarks>Modified Date is set and a new Version guid is set</remarks>
|
||||
internal override void UpdatingEntity()
|
||||
{
|
||||
base.UpdatingEntity();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
//TODO: THere's still a bunch of properties that don't exist in this use that need to be mapped somehow.
|
||||
|
||||
internal class UmbracoMembershipMember : MembershipUser
|
||||
{
|
||||
private readonly IMember _member;
|
||||
@@ -21,5 +23,44 @@ namespace Umbraco.Core.Models.Membership
|
||||
get { return _member.Email; }
|
||||
set { _member.Email = value; }
|
||||
}
|
||||
|
||||
public override object ProviderUserKey
|
||||
{
|
||||
get { return _member.Key; }
|
||||
}
|
||||
|
||||
public override System.DateTime CreationDate
|
||||
{
|
||||
get { return _member.CreateDate; }
|
||||
}
|
||||
|
||||
public override string UserName
|
||||
{
|
||||
get { return _member.Username; }
|
||||
}
|
||||
|
||||
public override string Comment
|
||||
{
|
||||
get { return _member.Comments; }
|
||||
set { _member.Comments = value; }
|
||||
}
|
||||
|
||||
public override bool IsApproved
|
||||
{
|
||||
get { return _member.IsApproved; }
|
||||
set { _member.IsApproved = value; }
|
||||
}
|
||||
|
||||
public override bool IsLockedOut
|
||||
{
|
||||
get { return _member.IsLockedOut; }
|
||||
}
|
||||
|
||||
public override System.DateTime LastLoginDate
|
||||
{
|
||||
get { return _member.LastLoginDate; }
|
||||
set { _member.LastLoginDate = value; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// be flagged as dirty.
|
||||
/// </summary>
|
||||
/// <param name="rememberPreviouslyChangedProperties"></param>
|
||||
internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
_addedSections.Clear();
|
||||
_removedSections.Clear();
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a paged result for a model collection
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[DataContract(Name = "pagedCollection", Namespace = "")]
|
||||
public class PagedResult<T>
|
||||
{
|
||||
public PagedResult(long totalItems, long pageNumber, long pageSize)
|
||||
{
|
||||
TotalItems = totalItems;
|
||||
PageNumber = pageNumber;
|
||||
PageSize = pageSize;
|
||||
|
||||
if (pageSize > 0)
|
||||
{
|
||||
TotalPages = (long) Math.Ceiling(totalItems/(Decimal) pageSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
TotalPages = 1;
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember(Name = "pageNumber")]
|
||||
public long PageNumber { get; private set; }
|
||||
|
||||
[DataMember(Name = "pageSize")]
|
||||
public long PageSize { get; private set; }
|
||||
|
||||
[DataMember(Name = "totalPages")]
|
||||
public long TotalPages { get; private set; }
|
||||
|
||||
[DataMember(Name = "totalItems")]
|
||||
public long TotalItems { get; private set; }
|
||||
|
||||
[DataMember(Name = "items")]
|
||||
public IEnumerable<T> Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the skip size based on the paged parameters specified
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns 0 if the page number or page size is zero
|
||||
/// </remarks>
|
||||
internal int SkipSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PageNumber > 0 && PageSize > 0)
|
||||
{
|
||||
return Convert.ToInt32((PageNumber - 1)*PageSize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a paged result for a model collection
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[DataContract(Name = "pagedCollection", Namespace = "")]
|
||||
public class PagedResult<T>
|
||||
{
|
||||
public PagedResult(long totalItems, long pageNumber, long pageSize)
|
||||
{
|
||||
TotalItems = totalItems;
|
||||
PageNumber = pageNumber;
|
||||
PageSize = pageSize;
|
||||
|
||||
if (pageSize > 0)
|
||||
{
|
||||
TotalPages = (long) Math.Ceiling(totalItems/(Decimal) pageSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
TotalPages = 1;
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember(Name = "pageNumber")]
|
||||
public long PageNumber { get; private set; }
|
||||
|
||||
[DataMember(Name = "pageSize")]
|
||||
public long PageSize { get; private set; }
|
||||
|
||||
[DataMember(Name = "totalPages")]
|
||||
public long TotalPages { get; private set; }
|
||||
|
||||
[DataMember(Name = "totalItems")]
|
||||
public long TotalItems { get; private set; }
|
||||
|
||||
[DataMember(Name = "items")]
|
||||
public IEnumerable<T> Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the skip size based on the paged parameters specified
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns 0 if the page number or page size is zero
|
||||
/// </remarks>
|
||||
internal int SkipSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PageNumber > 0 && PageSize > 0)
|
||||
{
|
||||
return Convert.ToInt32((PageNumber - 1)*PageSize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Models
|
||||
private readonly PropertyType _propertyType;
|
||||
private Guid _version;
|
||||
private object _value;
|
||||
private readonly PropertyTags _tagSupport = new PropertyTags();
|
||||
|
||||
protected Property()
|
||||
{
|
||||
@@ -44,6 +45,14 @@ namespace Umbraco.Core.Models
|
||||
private static readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo<Property, object>(x => x.Value);
|
||||
private static readonly PropertyInfo VersionSelector = ExpressionHelper.GetPropertyInfo<Property, Guid>(x => x.Version);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the instance of the tag support, by default tags are not enabled
|
||||
/// </summary>
|
||||
internal PropertyTags TagSupport
|
||||
{
|
||||
get { return _tagSupport; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Alias of the PropertyType, which this Property is based on
|
||||
/// </summary>
|
||||
|
||||
@@ -20,48 +20,34 @@ namespace Umbraco.Core.Models
|
||||
return property.ToXml(ApplicationContext.Current.Services.DataTypeService);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="Property"/> object
|
||||
/// </summary>
|
||||
/// <param name="property"><see cref="Property"/> to generate xml for</param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <returns>Xml of the property and its value</returns>
|
||||
internal static XElement ToXml(this Property property, IDataTypeService dataTypeService)
|
||||
{
|
||||
var nodeName = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias();
|
||||
|
||||
var xd = new XmlDocument();
|
||||
var xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, "");
|
||||
var xElement = new XElement(nodeName);
|
||||
|
||||
//Add the property alias to the legacy schema
|
||||
if (UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema)
|
||||
{
|
||||
var alias = xd.CreateAttribute("alias");
|
||||
alias.Value = property.Alias.ToSafeAlias();
|
||||
xmlNode.Attributes.Append(alias);
|
||||
var a = new XAttribute("alias", property.Alias.ToSafeAlias());
|
||||
xElement.Add(a);
|
||||
}
|
||||
|
||||
// * Get the XML result from the property editor if there is one, otherwise just construct a simple
|
||||
// XML construct from the value returned from the Property Editor.
|
||||
// More details discussed here: https://groups.google.com/forum/?fromgroups=#!topic/umbraco-dev/fieWZzHj7oY
|
||||
|
||||
//Get the property editor for thsi property and let it convert it to the xml structure
|
||||
var propertyEditor = PropertyEditorResolver.Current.GetByAlias(property.PropertyType.PropertyEditorAlias);
|
||||
if (propertyEditor != null)
|
||||
{
|
||||
var cacheValue = propertyEditor.ValueEditor.FormatValueForCache(property);
|
||||
|
||||
switch (property.PropertyType.DataTypeDatabaseType)
|
||||
{
|
||||
case DataTypeDatabaseType.Date:
|
||||
case DataTypeDatabaseType.Integer:
|
||||
xmlNode.AppendChild(xd.CreateTextNode(cacheValue.ToString()));
|
||||
break;
|
||||
case DataTypeDatabaseType.Nvarchar:
|
||||
case DataTypeDatabaseType.Ntext:
|
||||
//put text in cdata
|
||||
xmlNode.AppendChild(xd.CreateCDataSection(cacheValue.ToString()));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var xmlValue = propertyEditor.ValueEditor.ConvertDbToXml(property);
|
||||
xElement.Add(xmlValue);
|
||||
}
|
||||
|
||||
var element = xmlNode.GetXElement();
|
||||
return element;
|
||||
|
||||
return xElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A property extension class that allows us to enable tags for any given property
|
||||
/// </summary>
|
||||
internal class PropertyTags
|
||||
{
|
||||
public PropertyTags()
|
||||
{
|
||||
Enable = false;
|
||||
Behavior = PropertyTagBehavior.Merge;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The behavior of how to save the tags assigned -
|
||||
/// Merge (keep existing and append new),
|
||||
/// Remove (remove any of the tags in the Tags property that are currently assigned,
|
||||
/// Replace (replace the currently assigned tags with the ones specified)
|
||||
/// </summary>
|
||||
public PropertyTagBehavior Behavior { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flags the property to have tagging enabled
|
||||
/// </summary>
|
||||
public bool Enable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The actual tags to associate - tag/group
|
||||
/// </summary>
|
||||
public IEnumerable<Tuple<string, string>> Tags { get; set; }
|
||||
|
||||
}
|
||||
|
||||
internal enum PropertyTagBehavior
|
||||
{
|
||||
Replace,
|
||||
Remove,
|
||||
Merge
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to handle extended content.
|
||||
/// </summary>
|
||||
internal interface IPublishedContentExtended : IPublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a property to the extended content.
|
||||
/// </summary>
|
||||
/// <param name="property">The property to add.</param>
|
||||
void AddProperty(IPublishedProperty property);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether properties were added to the extended content.
|
||||
/// </summary>
|
||||
bool HasAddedProperties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the content set of the extended content.
|
||||
/// </summary>
|
||||
/// <param name="contentSet"></param>
|
||||
void SetContentSet(IEnumerable<IPublishedContent> contentSet);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the content set of the extended content.
|
||||
/// </summary>
|
||||
void ClearContentSet();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the index of the extended content.
|
||||
/// </summary>
|
||||
/// <param name="value">The index value.</param>
|
||||
void SetIndex(int value);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the index of the extended content.
|
||||
/// </summary>
|
||||
void ClearIndex();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the model creation service.
|
||||
/// </summary>
|
||||
internal interface IPublishedContentModelFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly-typed model representing a published content.
|
||||
/// </summary>
|
||||
/// <param name="content">The original published content.</param>
|
||||
/// <returns>The strongly-typed model representing the published content, or the published content
|
||||
/// itself it the factory has no model for that content type.</returns>
|
||||
IPublishedContent CreateModel(IPublishedContent content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
|
||||
public class PublishedContentExtended : PublishedContentWrapped, IPublishedContentExtended
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
// protected for models, private for our static Extend method
|
||||
protected PublishedContentExtended(IPublishedContent content)
|
||||
: base(content)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Index
|
||||
|
||||
private int? _index;
|
||||
|
||||
public override int GetIndex()
|
||||
{
|
||||
// fast
|
||||
if (_index.HasValue) return _index.Value;
|
||||
|
||||
// slow -- and don't cache, not in a set
|
||||
if (_contentSet == null) return Content.GetIndex();
|
||||
|
||||
// slow -- but cache for next time
|
||||
var index = _contentSet.FindIndex(x => x.Id == Id);
|
||||
if (index < 0)
|
||||
throw new IndexOutOfRangeException("Could not find content in the content set.");
|
||||
_index = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extend
|
||||
|
||||
internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable<IPublishedContent> contentSet)
|
||||
{
|
||||
var wrapped = content as PublishedContentExtended;
|
||||
while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false)
|
||||
wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended;
|
||||
|
||||
// if the factory returns something else than content it means it has created
|
||||
// a model, and then that model has to inherit from PublishedContentExtended,
|
||||
// => implements the internal IPublishedContentExtended.
|
||||
|
||||
var model = PublishedContentModelFactory.CreateModel(content);
|
||||
var extended = model == content // == means the factory did not create a model
|
||||
? new PublishedContentExtended(content) // so we have to extend
|
||||
: model; // else we can use what the factory returned
|
||||
|
||||
var extended2 = extended as IPublishedContentExtended;
|
||||
if (extended2 != null) // always true, but keeps Resharper happy
|
||||
extended2.SetContentSet(contentSet);
|
||||
return extended2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IPublishedContentExtended
|
||||
|
||||
void IPublishedContentExtended.AddProperty(IPublishedProperty property)
|
||||
{
|
||||
if (_properties == null)
|
||||
_properties = new Collection<IPublishedProperty>();
|
||||
_properties.Add(property);
|
||||
}
|
||||
|
||||
bool IPublishedContentExtended.HasAddedProperties
|
||||
{
|
||||
get { return _properties != null; }
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.SetContentSet(IEnumerable<IPublishedContent> contentSet)
|
||||
{
|
||||
_contentSet = contentSet;
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.ClearContentSet()
|
||||
{
|
||||
_contentSet = null;
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.SetIndex(int value)
|
||||
{
|
||||
_index = value;
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.ClearIndex()
|
||||
{
|
||||
_index = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content set
|
||||
|
||||
private IEnumerable<IPublishedContent> _contentSet;
|
||||
|
||||
public override IEnumerable<IPublishedContent> ContentSet
|
||||
{
|
||||
get { return _contentSet ?? Content.ContentSet; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
private ICollection<IPublishedProperty> _properties;
|
||||
|
||||
public override ICollection<IPublishedProperty> Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
return _properties == null
|
||||
? Content.Properties
|
||||
: Content.Properties.Union(_properties).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public override object this[string alias]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_properties != null)
|
||||
{
|
||||
var property = _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias));
|
||||
if (property != null) return property.HasValue ? property.Value : null;
|
||||
}
|
||||
return Content[alias];
|
||||
}
|
||||
}
|
||||
|
||||
public override IPublishedProperty GetProperty(string alias)
|
||||
{
|
||||
return _properties == null
|
||||
? Content.GetProperty(alias)
|
||||
: _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? Content.GetProperty(alias);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a strongly-typed published content.
|
||||
/// </summary>
|
||||
/// <remarks>Every strongly-typed published content class should inherit from <c>PublishedContentModel</c>
|
||||
/// (or inherit from a class that inherits from... etc.) so they are picked by the factory.</remarks>
|
||||
internal abstract class PublishedContentModel : PublishedContentExtended
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModel"/> class with
|
||||
/// an original <see cref="IPublishedContent"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="content">The original content.</param>
|
||||
protected PublishedContentModel(IPublishedContent content)
|
||||
: base(content)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the class is a published content model for a specified content type.
|
||||
/// </summary>
|
||||
/// <remarks>By default, the name of the class is assumed to be the content type alias. The
|
||||
/// <c>PublishedContentModelAttribute</c> can be used to indicate a different alias.</remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
internal sealed class PublishedContentModelAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelAttribute"/> class with a content type alias.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
public PublishedContentModelAttribute(string contentTypeAlias)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contentTypeAlias))
|
||||
throw new ArgumentException("Argument cannot be null nor empty.", "contentTypeAlias");
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content type alias.
|
||||
/// </summary>
|
||||
public string ContentTypeAlias { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides strongly typed published content models services.
|
||||
/// </summary>
|
||||
internal static class PublishedContentModelFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly typed published content model for an internal published content.
|
||||
/// </summary>
|
||||
/// <param name="content">The internal published content.</param>
|
||||
/// <returns>The strongly typed published content model.</returns>
|
||||
public static IPublishedContent CreateModel(IPublishedContent content)
|
||||
{
|
||||
return PublishedContentModelFactoryResolver.Current.HasValue
|
||||
? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a strongly typed content model factory
|
||||
/// </summary>
|
||||
internal class PublishedContentModelFactoryImpl : IPublishedContentModelFactory
|
||||
{
|
||||
//private readonly Dictionary<string, ConstructorInfo> _constructors
|
||||
// = new Dictionary<string, ConstructorInfo>();
|
||||
|
||||
private readonly Dictionary<string, Func<IPublishedContent, IPublishedContent>> _constructors
|
||||
= new Dictionary<string, Func<IPublishedContent, IPublishedContent>>();
|
||||
|
||||
public PublishedContentModelFactoryImpl()
|
||||
{
|
||||
var types = PluginManager.Current.ResolveTypes<PublishedContentModel>();
|
||||
var ctorArgTypes = new[] { typeof(IPublishedContent) };
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type.Inherits<PublishedContentModel>() == false)
|
||||
throw new InvalidOperationException(string.Format("Type {0} is marked with PublishedContentModel attribute but does not inherit from PublishedContentExtended.", type.FullName));
|
||||
var constructor = type.GetConstructor(ctorArgTypes);
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName));
|
||||
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
|
||||
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
|
||||
typeName = typeName.ToLowerInvariant();
|
||||
|
||||
if (_constructors.ContainsKey(typeName))
|
||||
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
|
||||
|
||||
// should work everywhere, but slow
|
||||
//_constructors[typeName] = constructor;
|
||||
|
||||
// much faster with a dynamic method but potential MediumTrust issues
|
||||
// here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object
|
||||
|
||||
// fast enough and works in MediumTrust
|
||||
// read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/
|
||||
var exprArg = Expression.Parameter(typeof(IPublishedContent), "content");
|
||||
var exprNew = Expression.New(constructor, exprArg);
|
||||
var expr = Expression.Lambda<Func<IPublishedContent, IPublishedContent>>(exprNew, exprArg);
|
||||
var func = expr.Compile();
|
||||
_constructors[typeName] = func;
|
||||
}
|
||||
}
|
||||
|
||||
public IPublishedContent CreateModel(IPublishedContent content)
|
||||
{
|
||||
// be case-insensitive
|
||||
var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant();
|
||||
|
||||
//ConstructorInfo constructor;
|
||||
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
// ? (IPublishedContent) constructor.Invoke(new object[] { content })
|
||||
// : content;
|
||||
|
||||
Func<IPublishedContent, IPublishedContent> constructor;
|
||||
return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
? constructor(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves the IPublishedContentModelFactory object.
|
||||
/// </summary>
|
||||
internal class PublishedContentModelFactoryResolver : SingleObjectResolverBase<PublishedContentModelFactoryResolver, IPublishedContentModelFactory>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelFactoryResolver"/>.
|
||||
/// </summary>
|
||||
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
|
||||
internal PublishedContentModelFactoryResolver()
|
||||
: base()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelFactoryResolver"/> with a factory.
|
||||
/// </summary>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
|
||||
internal PublishedContentModelFactoryResolver(IPublishedContentModelFactory factory)
|
||||
: base(factory)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the factory.
|
||||
/// </summary>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <remarks>For developers, at application startup.</remarks>
|
||||
public void SetFactory(IPublishedContentModelFactory factory)
|
||||
{
|
||||
Value = factory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the factory.
|
||||
/// </summary>
|
||||
public IPublishedContentModelFactory Factory
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an ordered set of <see cref="IPublishedContent"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of content.</typeparam>
|
||||
public class PublishedContentOrderedSet<T> : PublishedContentSet<T>, IOrderedEnumerable<T>
|
||||
where T : class, IPublishedContent
|
||||
{
|
||||
// ReSharper disable ParameterTypeCanBeEnumerable.Local
|
||||
internal PublishedContentOrderedSet(IOrderedEnumerable<T> content)
|
||||
// ReSharper restore ParameterTypeCanBeEnumerable.Local
|
||||
: base(content)
|
||||
{ }
|
||||
|
||||
// note: because we implement IOrderedEnumerable, we don't need to implement the ThenBy nor
|
||||
// ThenByDescending methods here, only CreateOrderedEnumerable and that does it.
|
||||
|
||||
#region IOrderedEnumerable<T>
|
||||
|
||||
public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(((IOrderedEnumerable<T>)Source).CreateOrderedEnumerable(keySelector, comparer, descending));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a set of <see cref="IPublishedContent"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of content.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>A <c>ContentSet{T}</c> is created from an <c>IEnumerable{T}</c> using the <c>ToContentSet</c>
|
||||
/// extension method.</para>
|
||||
/// <para>The content set source is enumerated only once. Same as what you get
|
||||
/// when you call ToList on an IEnumerable. Only, ToList enumerates its source when
|
||||
/// created, whereas a content set enumerates its source only when the content set itself
|
||||
/// is enumerated.</para>
|
||||
/// </remarks>
|
||||
public class PublishedContentSet<T> : IEnumerable<T>
|
||||
where T : class, IPublishedContent
|
||||
{
|
||||
// used by <c>ToContentSet</c> extension method to initialize a new set from an IEnumerable.
|
||||
internal PublishedContentSet(IEnumerable<T> source)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
Source = source;
|
||||
}
|
||||
|
||||
#region Source
|
||||
|
||||
protected readonly IEnumerable<T> Source;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enumerated
|
||||
|
||||
// cache the enumeration so we don't enumerate more than once. Same as what you get
|
||||
// when you call ToList on an IEnumerable. Only, ToList enumerates its source when
|
||||
// created, whereas a content set enumerates its source only when the content set itself
|
||||
// is enumerated.
|
||||
|
||||
// cache the wrapped items so if we reset the enumeration, we do not re-wrap everything (only new items).
|
||||
|
||||
private T[] _enumerated;
|
||||
private readonly Dictionary<T, IPublishedContentExtended> _xContent = new Dictionary<T, IPublishedContentExtended>();
|
||||
|
||||
// wrap an item, ie create the actual clone for this set
|
||||
private T MapContentAsT(T t)
|
||||
{
|
||||
return MapContent(t) as T;
|
||||
}
|
||||
|
||||
internal IPublishedContentExtended MapContent(T t)
|
||||
{
|
||||
IPublishedContentExtended extend;
|
||||
if (_xContent.TryGetValue(t, out extend)) return extend;
|
||||
|
||||
extend = PublishedContentExtended.Extend(t, this);
|
||||
var asT = extend as T;
|
||||
if (asT == null)
|
||||
throw new InvalidOperationException(string.Format("Failed extend a published content of type {0}."
|
||||
+ "Got {1} when expecting {2}.", t.GetType().FullName, extend.GetType().FullName, typeof(T).FullName));
|
||||
_xContent[t] = extend;
|
||||
return extend;
|
||||
}
|
||||
|
||||
private T[] Enumerated
|
||||
{
|
||||
get
|
||||
{
|
||||
// enumerate the source and cache the result
|
||||
// tell clones about their index within the set (for perfs purposes)
|
||||
var index = 0;
|
||||
return _enumerated ?? (_enumerated = Source.Select(t =>
|
||||
{
|
||||
var extend = MapContent(t);
|
||||
extend.SetIndex(index++);
|
||||
return extend as T;
|
||||
}).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
// indicates that the source has changed
|
||||
// so the set can clear its inner caches
|
||||
// should only be used by DynamicPublishedContentList
|
||||
internal void SourceChanged()
|
||||
{
|
||||
// reset the cached enumeration so it's enumerated again
|
||||
if (_enumerated == null) return;
|
||||
_enumerated = null;
|
||||
|
||||
foreach (var item in _xContent.Values)
|
||||
item.ClearIndex();
|
||||
|
||||
var removed = _xContent.Keys.Except(Source);
|
||||
foreach (var content in removed)
|
||||
{
|
||||
_xContent[content].ClearContentSet();
|
||||
_xContent.Remove(content);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in the set.
|
||||
/// </summary>
|
||||
/// <returns>The number of items in the set.</returns>
|
||||
/// <remarks>Will cause the set to be enumerated if it hasn't been already.</remarks>
|
||||
public virtual int Count
|
||||
{
|
||||
get { return Enumerated.Length; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<T>
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)Enumerated).GetEnumerator();
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wrap methods returning T
|
||||
|
||||
public T ElementAt(int index)
|
||||
{
|
||||
return MapContentAsT(Source.ElementAt(index));
|
||||
}
|
||||
|
||||
public T ElementAtOrDefault(int index)
|
||||
{
|
||||
var element = Source.ElementAtOrDefault(index);
|
||||
return element == null ? null : MapContentAsT(element);
|
||||
}
|
||||
|
||||
public T First()
|
||||
{
|
||||
return MapContentAsT(Source.First());
|
||||
}
|
||||
|
||||
public T First(Func<T, bool> predicate)
|
||||
{
|
||||
return MapContentAsT(Source.First(predicate));
|
||||
}
|
||||
|
||||
public T FirstOrDefault()
|
||||
{
|
||||
var first = Source.FirstOrDefault();
|
||||
return first == null ? null : MapContentAsT(first);
|
||||
}
|
||||
|
||||
public T FirstOrDefault(Func<T, bool> predicate)
|
||||
{
|
||||
var first = Source.FirstOrDefault(predicate);
|
||||
return first == null ? null : MapContentAsT(first);
|
||||
}
|
||||
|
||||
public T Last()
|
||||
{
|
||||
return MapContentAsT(Source.Last());
|
||||
}
|
||||
|
||||
public T Last(Func<T, bool> predicate)
|
||||
{
|
||||
return MapContentAsT(Source.Last(predicate));
|
||||
}
|
||||
|
||||
public T LastOrDefault()
|
||||
{
|
||||
var last = Source.LastOrDefault();
|
||||
return last == null ? null : MapContentAsT(last);
|
||||
}
|
||||
|
||||
public T LastOrDefault(Func<T, bool> predicate)
|
||||
{
|
||||
var last = Source.LastOrDefault(predicate);
|
||||
return last == null ? null : MapContentAsT(last);
|
||||
}
|
||||
|
||||
public T Single()
|
||||
{
|
||||
return MapContentAsT(Source.Single());
|
||||
}
|
||||
|
||||
public T Single(Func<T, bool> predicate)
|
||||
{
|
||||
return MapContentAsT(Source.Single(predicate));
|
||||
}
|
||||
|
||||
public T SingleOrDefault()
|
||||
{
|
||||
var single = Source.SingleOrDefault();
|
||||
return single == null ? null : MapContentAsT(single);
|
||||
}
|
||||
|
||||
public T SingleOrDefault(Func<T, bool> predicate)
|
||||
{
|
||||
var single = Source.SingleOrDefault(predicate);
|
||||
return single == null ? null : MapContentAsT(single);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wrap methods returning IOrderedEnumerable<T>
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderBy<TKey>(Func<T, TKey> keySelector)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderBy(keySelector));
|
||||
}
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderBy<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderBy(keySelector, comparer));
|
||||
}
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderByDescending<TKey>(Func<T, TKey> keySelector)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderByDescending(keySelector));
|
||||
}
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderByDescending<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderByDescending(keySelector, comparer));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using System.Web.UI;
|
||||
using Umbraco.Core.Cache;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an <see cref="IPublishedContent"/> type.
|
||||
/// </summary>
|
||||
/// <remarks>Instances of the <see cref="PublishedContentType"/> class are immutable, ie
|
||||
/// if the content type changes, then a new class needs to be created.</remarks>
|
||||
public class PublishedContentType
|
||||
{
|
||||
private readonly PublishedPropertyType[] _propertyTypes;
|
||||
|
||||
// fast alias-to-index xref containing both the raw alias and its lowercase version
|
||||
private readonly Dictionary<string, int> _indexes = new Dictionary<string, int>();
|
||||
|
||||
// internal so it can be used by PublishedNoCache which does _not_ want to cache anything and so will never
|
||||
// use the static cache getter PublishedContentType.GetPublishedContentType(alias) below - anything else
|
||||
// should use it.
|
||||
internal PublishedContentType(IContentTypeComposition contentType)
|
||||
{
|
||||
Id = contentType.Id;
|
||||
Alias = contentType.Alias;
|
||||
_propertyTypes = contentType.CompositionPropertyTypes
|
||||
.Select(x => new PublishedPropertyType(this, x))
|
||||
.ToArray();
|
||||
InitializeIndexes();
|
||||
}
|
||||
|
||||
// internal so it can be used for unit tests
|
||||
internal PublishedContentType(int id, string alias, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
{
|
||||
Id = id;
|
||||
Alias = alias;
|
||||
_propertyTypes = propertyTypes.ToArray();
|
||||
foreach (var propertyType in _propertyTypes)
|
||||
propertyType.ContentType = this;
|
||||
InitializeIndexes();
|
||||
}
|
||||
|
||||
private void InitializeIndexes()
|
||||
{
|
||||
for (var i = 0; i < _propertyTypes.Length; i++)
|
||||
{
|
||||
var propertyType = _propertyTypes[i];
|
||||
_indexes[propertyType.PropertyTypeAlias] = i;
|
||||
_indexes[propertyType.PropertyTypeAlias.ToLowerInvariant()] = i;
|
||||
}
|
||||
}
|
||||
|
||||
#region Content type
|
||||
|
||||
public int Id { get; private set; }
|
||||
public string Alias { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public IEnumerable<PublishedPropertyType> PropertyTypes
|
||||
{
|
||||
get { return _propertyTypes; }
|
||||
}
|
||||
|
||||
// alias is case-insensitive
|
||||
// this is the ONLY place where we compare ALIASES!
|
||||
public int GetPropertyIndex(string alias)
|
||||
{
|
||||
int index;
|
||||
if (_indexes.TryGetValue(alias, out index)) return index; // fastest
|
||||
if (_indexes.TryGetValue(alias.ToLowerInvariant(), out index)) return index; // slower
|
||||
return -1;
|
||||
}
|
||||
|
||||
// virtual for unit tests
|
||||
public virtual PublishedPropertyType GetPropertyType(string alias)
|
||||
{
|
||||
var index = GetPropertyIndex(alias);
|
||||
return GetPropertyType(index);
|
||||
}
|
||||
|
||||
// virtual for unit tests
|
||||
public virtual PublishedPropertyType GetPropertyType(int index)
|
||||
{
|
||||
return index >= 0 && index < _propertyTypes.Length ? _propertyTypes[index] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cache
|
||||
|
||||
// these methods are called by ContentTypeCacheRefresher and DataTypeCacheRefresher
|
||||
|
||||
internal static void ClearAll()
|
||||
{
|
||||
Logging.LogHelper.Debug<PublishedContentType>("Clear all.");
|
||||
// ok and faster to do it by types, assuming noone else caches PublishedContentType instances
|
||||
//ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_");
|
||||
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>();
|
||||
}
|
||||
|
||||
internal static void ClearContentType(int id)
|
||||
{
|
||||
Logging.LogHelper.Debug<PublishedContentType>("Clear content type w/id {0}.", () => id);
|
||||
// requires a predicate because the key does not contain the ID
|
||||
// faster than key strings comparisons anyway
|
||||
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
|
||||
(key, value) => value.Id == id);
|
||||
}
|
||||
|
||||
internal static void ClearDataType(int id)
|
||||
{
|
||||
Logging.LogHelper.Debug<PublishedContentType>("Clear data type w/id {0}.", () => id);
|
||||
// there is no recursion to handle here because a PublishedContentType contains *all* its
|
||||
// properties ie both its own properties and those that were inherited (it's based upon an
|
||||
// IContentTypeComposition) and so every PublishedContentType having a property based upon
|
||||
// the cleared data type, be it local or inherited, will be cleared.
|
||||
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
|
||||
(key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id));
|
||||
}
|
||||
|
||||
public static PublishedContentType Get(PublishedItemType itemType, string alias)
|
||||
{
|
||||
var key = string.Format("PublishedContentType_{0}_{1}",
|
||||
itemType == PublishedItemType.Content ? "content" : "media", alias.ToLowerInvariant());
|
||||
|
||||
var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem<PublishedContentType>(key,
|
||||
() => CreatePublishedContentType(itemType, alias));
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private static PublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias)
|
||||
{
|
||||
if (GetPublishedContentTypeCallback != null)
|
||||
return GetPublishedContentTypeCallback(alias);
|
||||
|
||||
var contentType = itemType == PublishedItemType.Content
|
||||
? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias)
|
||||
: (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias);
|
||||
|
||||
return new PublishedContentType(contentType);
|
||||
}
|
||||
|
||||
// for unit tests - changing the callback must reset the cache obviously
|
||||
private static Func<string, PublishedContentType> _getPublishedContentTypeCallBack;
|
||||
internal static Func<string, PublishedContentType> GetPublishedContentTypeCallback
|
||||
{
|
||||
get { return _getPublishedContentTypeCallBack; }
|
||||
set
|
||||
{
|
||||
// see note above
|
||||
//ClearAll();
|
||||
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_");
|
||||
|
||||
_getPublishedContentTypeCallBack = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
//
|
||||
// This class has two purposes.
|
||||
//
|
||||
// - First, we cannot implement strongly-typed content by inheriting from some sort
|
||||
// of "master content" because that master content depends on the actual content cache
|
||||
// that is being used. It can be an XmlPublishedContent with the XmlPublishedCache,
|
||||
// or just anything else.
|
||||
//
|
||||
// So we implement strongly-typed content by encapsulating whatever content is
|
||||
// returned by the content cache, and providing extra properties (mostly) or
|
||||
// methods or whatever. This class provides the base for such encapsulation.
|
||||
//
|
||||
// - Second, any time a content is used in a content set obtained from
|
||||
// IEnumerable<IPublishedContent>.ToContentSet(), it needs to be cloned and extended
|
||||
// in order to know about its position in the set. This class provides the base
|
||||
// for implementing such extension.
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Provides an abstract base class for <c>IPublishedContent</c> implementations that
|
||||
/// wrap and extend another <c>IPublishedContent</c>.
|
||||
/// </summary>
|
||||
public abstract class PublishedContentWrapped : IPublishedContent
|
||||
{
|
||||
protected readonly IPublishedContent Content;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="PublishedContentWrapped"/> class
|
||||
/// with an <c>IPublishedContent</c> instance to wrap and extend.
|
||||
/// </summary>
|
||||
/// <param name="content">The content to wrap and extend.</param>
|
||||
protected PublishedContentWrapped(IPublishedContent content)
|
||||
{
|
||||
Content = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped content.
|
||||
/// </summary>
|
||||
/// <returns>The wrapped content, that was passed as an argument to the constructor.</returns>
|
||||
public IPublishedContent Unwrap()
|
||||
{
|
||||
return Content;
|
||||
}
|
||||
|
||||
#region ContentSet
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> ContentSet
|
||||
{
|
||||
get { return Content.ContentSet; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentType
|
||||
|
||||
public virtual PublishedContentType ContentType { get { return Content.ContentType; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content
|
||||
|
||||
public virtual int Id
|
||||
{
|
||||
get { return Content.Id; }
|
||||
}
|
||||
|
||||
public virtual int TemplateId
|
||||
{
|
||||
get { return Content.TemplateId; }
|
||||
}
|
||||
|
||||
public virtual int SortOrder
|
||||
{
|
||||
get { return Content.SortOrder; }
|
||||
}
|
||||
|
||||
public virtual string Name
|
||||
{
|
||||
get { return Content.Name; }
|
||||
}
|
||||
|
||||
public virtual string UrlName
|
||||
{
|
||||
get { return Content.UrlName; }
|
||||
}
|
||||
|
||||
public virtual string DocumentTypeAlias
|
||||
{
|
||||
get { return Content.DocumentTypeAlias; }
|
||||
}
|
||||
|
||||
public virtual int DocumentTypeId
|
||||
{
|
||||
get { return Content.DocumentTypeId; }
|
||||
}
|
||||
|
||||
public virtual string WriterName
|
||||
{
|
||||
get { return Content.WriterName; }
|
||||
}
|
||||
|
||||
public virtual string CreatorName
|
||||
{
|
||||
get { return Content.CreatorName; }
|
||||
}
|
||||
|
||||
public virtual int WriterId
|
||||
{
|
||||
get { return Content.WriterId; }
|
||||
}
|
||||
|
||||
public virtual int CreatorId
|
||||
{
|
||||
get { return Content.CreatorId; }
|
||||
}
|
||||
|
||||
public virtual string Path
|
||||
{
|
||||
get { return Content.Path; }
|
||||
}
|
||||
|
||||
public virtual DateTime CreateDate
|
||||
{
|
||||
get { return Content.CreateDate; }
|
||||
}
|
||||
|
||||
public virtual DateTime UpdateDate
|
||||
{
|
||||
get { return Content.UpdateDate; }
|
||||
}
|
||||
|
||||
public virtual Guid Version
|
||||
{
|
||||
get { return Content.Version; }
|
||||
}
|
||||
|
||||
public virtual int Level
|
||||
{
|
||||
get { return Content.Level; }
|
||||
}
|
||||
|
||||
public virtual string Url
|
||||
{
|
||||
get { return Content.Url; }
|
||||
}
|
||||
|
||||
public virtual PublishedItemType ItemType
|
||||
{
|
||||
get { return Content.ItemType; }
|
||||
}
|
||||
|
||||
public virtual bool IsDraft
|
||||
{
|
||||
get { return Content.IsDraft; }
|
||||
}
|
||||
|
||||
public virtual int GetIndex()
|
||||
{
|
||||
return Content.GetIndex();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tree
|
||||
|
||||
public virtual IPublishedContent Parent
|
||||
{
|
||||
get { return Content.Parent; }
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> Children
|
||||
{
|
||||
get { return Content.Children; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public virtual ICollection<IPublishedProperty> Properties
|
||||
{
|
||||
get { return Content.Properties; }
|
||||
}
|
||||
|
||||
public virtual object this[string alias]
|
||||
{
|
||||
get { return Content[alias]; }
|
||||
}
|
||||
|
||||
public virtual IPublishedProperty GetProperty(string alias)
|
||||
{
|
||||
return Content.GetProperty(alias);
|
||||
}
|
||||
|
||||
public virtual IPublishedProperty GetProperty(string alias, bool recurse)
|
||||
{
|
||||
return Content.GetProperty(alias, recurse);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for <c>IPublishedProperty</c> implementations which converts and caches
|
||||
/// the value source to the actual value to use when rendering content.
|
||||
/// </summary>
|
||||
internal abstract class PublishedPropertyBase : IPublishedProperty
|
||||
{
|
||||
public readonly PublishedPropertyType PropertyType;
|
||||
|
||||
protected PublishedPropertyBase(PublishedPropertyType propertyType)
|
||||
{
|
||||
if (propertyType == null)
|
||||
throw new ArgumentNullException("propertyType");
|
||||
PropertyType = propertyType;
|
||||
}
|
||||
|
||||
public string PropertyTypeAlias
|
||||
{
|
||||
get { return PropertyType.PropertyTypeAlias; }
|
||||
}
|
||||
|
||||
// these have to be provided by the actual implementation
|
||||
public abstract bool HasValue { get; }
|
||||
public abstract object DataValue { get; }
|
||||
public abstract object Value { get; }
|
||||
public abstract object XPathValue { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an <see cref="IPublishedProperty"/> type.
|
||||
/// </summary>
|
||||
/// <remarks>Instances of the <see cref="PublishedPropertyType"/> class are immutable, ie
|
||||
/// if the property type changes, then a new class needs to be created.</remarks>
|
||||
public class PublishedPropertyType
|
||||
{
|
||||
public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType)
|
||||
{
|
||||
// PropertyEditor [1:n] DataTypeDefinition [1:n] PropertyType
|
||||
|
||||
ContentType = contentType;
|
||||
PropertyTypeAlias = propertyType.Alias;
|
||||
|
||||
DataTypeId = propertyType.DataTypeDefinitionId;
|
||||
PropertyEditorAlias = propertyType.PropertyEditorAlias;
|
||||
|
||||
InitializeConverters();
|
||||
}
|
||||
|
||||
// for unit tests
|
||||
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias)
|
||||
{
|
||||
// ContentType to be set by PublishedContentType when creating it
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
|
||||
DataTypeId = dataTypeDefinitionId;
|
||||
PropertyEditorAlias = propertyEditorAlias;
|
||||
|
||||
InitializeConverters();
|
||||
}
|
||||
|
||||
#region Property type
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the published content type containing the property type.
|
||||
/// </summary>
|
||||
// internally set by PublishedContentType constructor
|
||||
public PublishedContentType ContentType { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias uniquely identifying the property type.
|
||||
/// </summary>
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier uniquely identifying the data type supporting the property type.
|
||||
/// </summary>
|
||||
public int DataTypeId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias uniquely identifying the property editor for the property type.
|
||||
/// </summary>
|
||||
public string PropertyEditorAlias { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Converters
|
||||
|
||||
private IPropertyValueConverter _converter;
|
||||
|
||||
private PropertyCacheLevel _sourceCacheLevel;
|
||||
private PropertyCacheLevel _objectCacheLevel;
|
||||
private PropertyCacheLevel _xpathCacheLevel;
|
||||
|
||||
private void InitializeConverters()
|
||||
{
|
||||
var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
|
||||
|
||||
// todo: remove Union() once we drop IPropertyEditorValueConverter support.
|
||||
_converter = null;
|
||||
foreach (var converter in converters.Union(GetCompatConverters()).Where(x => x.IsConverter(this)))
|
||||
{
|
||||
if (_converter == null)
|
||||
{
|
||||
_converter = converter;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("More than one converter for property type {0}.{1}",
|
||||
ContentType.Alias, PropertyTypeAlias));
|
||||
}
|
||||
}
|
||||
|
||||
// get the cache levels, quietely fixing the inconsistencies (no need to throw, really)
|
||||
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
|
||||
if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel;
|
||||
if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel;
|
||||
}
|
||||
|
||||
static PropertyCacheLevel GetCacheLevel(IPropertyValueConverter converter, PropertyCacheValue value)
|
||||
{
|
||||
if (converter == null)
|
||||
return PropertyCacheLevel.Request;
|
||||
|
||||
var attr = converter.GetType().GetCustomAttributes<PropertyValueCacheAttribute>(false)
|
||||
.FirstOrDefault(x => x.Value == value || x.Value == PropertyCacheValue.All);
|
||||
|
||||
return attr == null ? PropertyCacheLevel.Request : attr.Level;
|
||||
}
|
||||
|
||||
// converts the raw value into the source value
|
||||
// uses converters, else falls back to dark (& performance-wise expensive) magic
|
||||
// source: the property raw value
|
||||
// preview: whether we are previewing or not
|
||||
public object ConvertDataToSource(object source, bool preview)
|
||||
{
|
||||
// use the converter else use dark (& performance-wise expensive) magic
|
||||
return _converter != null
|
||||
? _converter.ConvertDataToSource(this, source, preview)
|
||||
: ConvertUsingDarkMagic(source);
|
||||
}
|
||||
|
||||
// gets the source cache level
|
||||
public PropertyCacheLevel SourceCacheLevel { get { return _sourceCacheLevel; } }
|
||||
|
||||
// converts the source value into the clr value
|
||||
// uses converters, else returns the source value
|
||||
// source: the property source value
|
||||
// preview: whether we are previewing or not
|
||||
public object ConvertSourceToObject(object source, bool preview)
|
||||
{
|
||||
// use the converter if any
|
||||
// else just return the source value
|
||||
return _converter != null
|
||||
? _converter.ConvertSourceToObject(this, source, preview)
|
||||
: source;
|
||||
}
|
||||
|
||||
// gets the value cache level
|
||||
public PropertyCacheLevel ObjectCacheLevel { get { return _objectCacheLevel; } }
|
||||
|
||||
// converts the source value into the xpath value
|
||||
// uses the converter else returns the source value as a string
|
||||
// if successful, returns either a string or an XPathNavigator
|
||||
// source: the property source value
|
||||
// preview: whether we are previewing or not
|
||||
public object ConvertSourceToXPath(object source, bool preview)
|
||||
{
|
||||
// use the converter if any
|
||||
if (_converter != null)
|
||||
return _converter.ConvertSourceToXPath(this, source, preview);
|
||||
|
||||
// else just return the source value as a string or an XPathNavigator
|
||||
if (source == null) return null;
|
||||
var xElement = source as XElement;
|
||||
if (xElement != null)
|
||||
return xElement.CreateNavigator();
|
||||
return source.ToString().Trim();
|
||||
}
|
||||
|
||||
// gets the xpath cache level
|
||||
public PropertyCacheLevel XPathCacheLevel { get { return _xpathCacheLevel; } }
|
||||
|
||||
internal static object ConvertUsingDarkMagic(object source)
|
||||
{
|
||||
// convert to string
|
||||
var stringSource = source as string;
|
||||
if (stringSource == null) return source; // not a string => return the object
|
||||
stringSource = stringSource.Trim();
|
||||
if (stringSource.Length == 0) return null; // empty string => return null
|
||||
|
||||
// try numbers and booleans
|
||||
// make sure we use the invariant culture ie a dot decimal point, comma is for csv
|
||||
// NOTE far from perfect: "01a" is returned as a string but "012" is returned as an integer...
|
||||
int i;
|
||||
if (int.TryParse(stringSource, NumberStyles.Integer, CultureInfo.InvariantCulture, out i))
|
||||
return i;
|
||||
float f;
|
||||
if (float.TryParse(stringSource, NumberStyles.Float, CultureInfo.InvariantCulture, out f))
|
||||
return f;
|
||||
bool b;
|
||||
if (bool.TryParse(stringSource, out b))
|
||||
return b;
|
||||
|
||||
// try xml - that is expensive, performance-wise
|
||||
XElement elt;
|
||||
if (XmlHelper.TryCreateXElementFromPropertyValue(stringSource, out elt))
|
||||
return new DynamicXml(elt); // xml => return DynamicXml for compatiblity's sake
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Compat
|
||||
|
||||
// backward-compatibility: support IPropertyEditorValueConverter while we have to
|
||||
// todo: remove once we drop IPropertyEditorValueConverter support.
|
||||
|
||||
IEnumerable<IPropertyValueConverter> GetCompatConverters()
|
||||
{
|
||||
var propertyEditorGuid = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(PropertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ReturnNull);
|
||||
return PropertyEditorValueConvertersResolver.HasCurrent && propertyEditorGuid.HasValue
|
||||
? PropertyEditorValueConvertersResolver.Current.Converters
|
||||
.Where(x => x.IsConverterFor(propertyEditorGuid.Value, ContentType.Alias, PropertyTypeAlias))
|
||||
.Select(x => new CompatConverter(x))
|
||||
: Enumerable.Empty<IPropertyValueConverter>();
|
||||
}
|
||||
|
||||
class CompatConverter : PropertyValueConverterBase
|
||||
{
|
||||
private readonly IPropertyEditorValueConverter _converter;
|
||||
|
||||
public CompatConverter(IPropertyEditorValueConverter converter)
|
||||
{
|
||||
_converter = converter;
|
||||
}
|
||||
|
||||
public override bool IsConverter(PublishedPropertyType propertyType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// NOTE: ignore preview, because IPropertyEditorValueConverter does not support it
|
||||
return _converter.ConvertPropertyValue(source).Result;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,18 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of published item
|
||||
/// The type of published content, ie whether it is a content or a media.
|
||||
/// </summary>
|
||||
public enum PublishedItemType
|
||||
{
|
||||
/// <summary>
|
||||
/// A content, ie what was formerly known as a document.
|
||||
/// </summary>
|
||||
Content,
|
||||
|
||||
/// <summary>
|
||||
/// A media.
|
||||
/// </summary>
|
||||
Media
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The definition of a published property
|
||||
/// </summary>
|
||||
public class PublishedPropertyDefinition
|
||||
{
|
||||
public PublishedPropertyDefinition(string propertyTypeAlias, string documentTypeAlias, string propertyEditorAlias)
|
||||
{
|
||||
//PropertyId = propertyId;
|
||||
DocumentTypeAlias = documentTypeAlias;
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
PropertyEditorAlias = propertyEditorAlias;
|
||||
//ItemType = itemType;
|
||||
}
|
||||
|
||||
//public int PropertyId { get; private set; }
|
||||
public string DocumentTypeAlias { get; private set; }
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
public string PropertyEditorAlias { get; private set; }
|
||||
//public PublishedItemType ItemType { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("ContentId")]
|
||||
[ForeignKey(typeof(ContentDto), Column = "nodeId")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsContentVersion_ContentId")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("VersionId")]
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("published")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsDocument_published")]
|
||||
public bool Published { get; set; }
|
||||
|
||||
[Column("documentUser")]
|
||||
@@ -51,6 +52,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("newest")]
|
||||
[Constraint(Default = "0")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsDocument_newest")]
|
||||
public bool Newest { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
|
||||
@@ -15,11 +15,12 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("trashed")]
|
||||
[Constraint(Default = "0")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoNodeTrashed")]
|
||||
public bool Trashed { get; set; }
|
||||
|
||||
[Column("parentID")]
|
||||
[ForeignKey(typeof(NodeDto))]
|
||||
[IndexAttribute(IndexTypes.NonClustered, Name = "IX_umbracoNodeParentId")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoNodeParentId")]
|
||||
public int ParentId { get; set; }
|
||||
|
||||
[Column("nodeUser")]
|
||||
@@ -46,7 +47,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("nodeObjectType")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[IndexAttribute(IndexTypes.NonClustered, Name = "IX_umbracoNodeObjectType")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoNodeObjectType")]
|
||||
public Guid? NodeObjectType { get; set; }
|
||||
|
||||
[Column("createDate")]
|
||||
|
||||
@@ -18,5 +18,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("PropertyGroupSortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
[Column("contenttypeNodeId")]
|
||||
public int ContentTypeNodeId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("contentTypeId")]
|
||||
public int ContentTypeId { get; set; }
|
||||
|
||||
[Column("propertyTypeGroupId")]
|
||||
[Column("PropertyTypesGroupId")]
|
||||
public int? PropertyTypeGroupId { get; set; }
|
||||
|
||||
[Column("Alias")]
|
||||
|
||||
@@ -15,10 +15,12 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("tag")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(200)]
|
||||
[Index(IndexTypes.NonClustered, ForColumns = "tag,group", Name = "IX_cmsTags")]
|
||||
public string Tag { get; set; }//NOTE Is set to [varchar] (200) in Sql Server script
|
||||
|
||||
[Column("ParentId")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[ForeignKey(typeof(TagDto), Name = "FK_cmsTags_cmsTags")]
|
||||
public int? ParentId { get; set; }
|
||||
|
||||
[Column("group")]
|
||||
|
||||
@@ -9,12 +9,16 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
internal class TagRelationshipDto
|
||||
{
|
||||
[Column("nodeId")]
|
||||
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsTagRelationship", OnColumns = "nodeId, tagId")]
|
||||
[ForeignKey(typeof(NodeDto))]
|
||||
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsTagRelationship", OnColumns = "nodeId, propertyTypeId, tagId")]
|
||||
[ForeignKey(typeof(ContentDto), Name = "FK_cmsTagRelationship_cmsContent", Column = "nodeId")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("tagId")]
|
||||
[ForeignKey(typeof(TagDto))]
|
||||
public int TagId { get; set; }
|
||||
|
||||
[Column("propertyTypeId")]
|
||||
[ForeignKey(typeof(PropertyTypeDto), Name = "FK_cmsTagRelationship_cmsPropertyType")]
|
||||
public int PropertyTypeId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class Tag : Entity, ITag
|
||||
{
|
||||
public Tag()
|
||||
{
|
||||
}
|
||||
|
||||
public Tag(int id, string text, string @group)
|
||||
{
|
||||
Text = text;
|
||||
Group = @group;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo TextSelector = ExpressionHelper.GetPropertyInfo<Tag, string>(x => x.Text);
|
||||
private static readonly PropertyInfo GroupSelector = ExpressionHelper.GetPropertyInfo<Tag, string>(x => x.Group);
|
||||
private string _text;
|
||||
private string _group;
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return _text; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_text = value;
|
||||
return _text;
|
||||
}, _text, TextSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public string Group
|
||||
{
|
||||
get { return _group; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_group = value;
|
||||
return _group;
|
||||
}, _group, GroupSelector);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: enable this at some stage
|
||||
//public int ParentId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -44,10 +44,12 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public UmbracoEntity()
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public UmbracoEntity(bool trashed)
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
Trashed = trashed;
|
||||
}
|
||||
|
||||
@@ -142,6 +144,9 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
|
||||
public bool HasChildren
|
||||
{
|
||||
get { return _hasChildren; }
|
||||
@@ -152,6 +157,9 @@ namespace Umbraco.Core.Models
|
||||
_hasChildren = value;
|
||||
return _hasChildren;
|
||||
}, _hasChildren, HasChildrenSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["HasChildren"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +172,10 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_isPublished = value;
|
||||
return _isPublished;
|
||||
}, _isPublished, IsPublishedSelector);
|
||||
}, _isPublished, IsPublishedSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["IsPublished"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +189,9 @@ namespace Umbraco.Core.Models
|
||||
_isDraft = value;
|
||||
return _isDraft;
|
||||
}, _isDraft, IsDraftSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["IsDraft"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +205,9 @@ namespace Umbraco.Core.Models
|
||||
_hasPendingChanges = value;
|
||||
return _hasPendingChanges;
|
||||
}, _hasPendingChanges, HasPendingChangesSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["HasPendingChanges"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +221,9 @@ namespace Umbraco.Core.Models
|
||||
_contentTypeAlias = value;
|
||||
return _contentTypeAlias;
|
||||
}, _contentTypeAlias, ContentTypeAliasSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["ContentTypeAlias"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,6 +237,9 @@ namespace Umbraco.Core.Models
|
||||
_contentTypeIcon = value;
|
||||
return _contentTypeIcon;
|
||||
}, _contentTypeIcon, ContentTypeIconSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["ContentTypeIcon"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +253,9 @@ namespace Umbraco.Core.Models
|
||||
_contentTypeThumbnail = value;
|
||||
return _contentTypeThumbnail;
|
||||
}, _contentTypeThumbnail, ContentTypeThumbnailSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["ContentTypeThumbnail"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +268,16 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_nodeObjectTypeId = value;
|
||||
return _nodeObjectTypeId;
|
||||
}, _nodeObjectTypeId, NodeObjectTypeIdSelector);
|
||||
}, _nodeObjectTypeId, NodeObjectTypeIdSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
AdditionalData["NodeObjectTypeId"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
public IList<UmbracoProperty> UmbracoProperties { get; set; }
|
||||
|
||||
internal class UmbracoProperty
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
@@ -9,7 +10,8 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
public static class UmbracoObjectTypesExtensions
|
||||
{
|
||||
private static readonly Dictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new Dictionary<UmbracoObjectTypes,Guid>();
|
||||
//MUST be concurrent to avoid thread collisions!
|
||||
private static readonly ConcurrentDictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new ConcurrentDictionary<UmbracoObjectTypes, Guid>();
|
||||
|
||||
/// <summary>
|
||||
/// Get an UmbracoObjectTypes value from it's name
|
||||
@@ -48,24 +50,22 @@ namespace Umbraco.Core.Models
|
||||
/// <returns>a GUID value of the UmbracoObjectTypes</returns>
|
||||
public static Guid GetGuid(this UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
if (UmbracoObjectTypeCache.ContainsKey(umbracoObjectType))
|
||||
return UmbracoObjectTypeCache[umbracoObjectType];
|
||||
return UmbracoObjectTypeCache.GetOrAdd(umbracoObjectType, types =>
|
||||
{
|
||||
var type = typeof(UmbracoObjectTypes);
|
||||
var memInfo = type.GetMember(umbracoObjectType.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
|
||||
false);
|
||||
|
||||
var type = typeof(UmbracoObjectTypes);
|
||||
var memInfo = type.GetMember(umbracoObjectType.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
|
||||
false);
|
||||
if (attributes.Length == 0)
|
||||
return Guid.Empty;
|
||||
|
||||
if (attributes.Length == 0)
|
||||
return Guid.Empty;
|
||||
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
|
||||
if (attribute == null)
|
||||
return Guid.Empty;
|
||||
|
||||
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
|
||||
if (attribute == null)
|
||||
return Guid.Empty;
|
||||
|
||||
UmbracoObjectTypeCache.Add(umbracoObjectType, attribute.ObjectId);
|
||||
|
||||
return attribute.ObjectId;
|
||||
return attribute.ObjectId;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -169,47 +169,46 @@ namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
// ensure we can
|
||||
if (CanResolveBeforeFrozen == false)
|
||||
Resolution.EnsureIsFrozen();
|
||||
using (Resolution.Reader(CanResolveBeforeFrozen))
|
||||
{
|
||||
// note: we apply .ToArray() to the output of CreateInstance() because that is an IEnumerable that
|
||||
// comes from the PluginManager we want to be _sure_ that it's not a Linq of some sort, but the
|
||||
// instances have actually been instanciated when we return.
|
||||
|
||||
// note: we apply .ToArray() to the output of CreateInstance() because that is an IEnumerable that
|
||||
// comes from the PluginManager we want to be _sure_ that it's not a Linq of some sort, but the
|
||||
// instances have actually been instanciated when we return.
|
||||
switch (LifetimeScope)
|
||||
{
|
||||
case ObjectLifetimeScope.HttpRequest:
|
||||
// create new instances per HttpContext
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
// create if not already there
|
||||
if (CurrentHttpContext.Items[_httpContextKey] == null)
|
||||
{
|
||||
l.UpgradeToWriteLock();
|
||||
CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray();
|
||||
}
|
||||
return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
|
||||
}
|
||||
|
||||
switch (LifetimeScope)
|
||||
{
|
||||
case ObjectLifetimeScope.HttpRequest:
|
||||
// create new instances per HttpContext
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
// create if not already there
|
||||
if (CurrentHttpContext.Items[_httpContextKey] == null)
|
||||
{
|
||||
l.UpgradeToWriteLock();
|
||||
CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray();
|
||||
}
|
||||
return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
|
||||
}
|
||||
case ObjectLifetimeScope.Application:
|
||||
// create new instances per application
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
// create if not already there
|
||||
if (_applicationInstances == null)
|
||||
{
|
||||
l.UpgradeToWriteLock();
|
||||
_applicationInstances = CreateInstances().ToArray();
|
||||
}
|
||||
return _applicationInstances;
|
||||
}
|
||||
|
||||
case ObjectLifetimeScope.Application:
|
||||
// create new instances per application
|
||||
using(var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
// create if not already there
|
||||
if (_applicationInstances == null)
|
||||
{
|
||||
l.UpgradeToWriteLock();
|
||||
_applicationInstances = CreateInstances().ToArray();
|
||||
}
|
||||
return _applicationInstances;
|
||||
}
|
||||
|
||||
case ObjectLifetimeScope.Transient:
|
||||
default:
|
||||
// create new instances each time
|
||||
return CreateInstances().ToArray();
|
||||
}
|
||||
case ObjectLifetimeScope.Transient:
|
||||
default:
|
||||
// create new instances each time
|
||||
return CreateInstances().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +458,21 @@ namespace Umbraco.Core.ObjectResolution
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types in the collection of types.
|
||||
/// </summary>
|
||||
/// <returns>The types in the collection of types.</returns>
|
||||
/// <remarks>Returns an enumeration, the list cannot be modified.</remarks>
|
||||
public virtual IEnumerable<Type> GetTypes()
|
||||
{
|
||||
Type[] types;
|
||||
using (new ReadLock(_lock))
|
||||
{
|
||||
types = _instanceTypes.ToArray();
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the specified type is already in the collection of types.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
@@ -12,8 +14,8 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </remarks>
|
||||
internal static class Resolution
|
||||
{
|
||||
private static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
|
||||
private volatile static bool _isFrozen;
|
||||
private static readonly ReaderWriterLockSlim ConfigurationLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
private volatile static bool _isFrozen;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when resolution is frozen.
|
||||
@@ -24,18 +26,26 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether resolution of objects is frozen.
|
||||
/// </summary>
|
||||
public static bool IsFrozen
|
||||
// internal for unit tests, use ReadFrozen if you want to be sure
|
||||
internal static bool IsFrozen
|
||||
{
|
||||
get { return _isFrozen; }
|
||||
private set { _isFrozen = value; }
|
||||
get
|
||||
{
|
||||
using (new ReadLock(ConfigurationLock))
|
||||
{
|
||||
return _isFrozen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnsureIsFrozen()
|
||||
{
|
||||
if (!_isFrozen)
|
||||
throw new InvalidOperationException("Resolution is not frozen, it is not yet possible to get values from it.");
|
||||
public static IDisposable Reader(bool canReadUnfrozen = false)
|
||||
{
|
||||
IDisposable l = new ReadLock(ConfigurationLock);
|
||||
if (canReadUnfrozen || _isFrozen) return l;
|
||||
|
||||
}
|
||||
l.Dispose();
|
||||
throw new InvalidOperationException("Resolution is not frozen, it is not yet possible to get values from it.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a disposable object that represents safe access to unfrozen resolution configuration.
|
||||
@@ -45,13 +55,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
IDisposable l = new WriteLock(_lock);
|
||||
if (_isFrozen)
|
||||
{
|
||||
l.Dispose();
|
||||
throw new InvalidOperationException("Resolution is frozen, it is not possible to configure it anymore.");
|
||||
}
|
||||
return l;
|
||||
IDisposable l = new WriteLock(ConfigurationLock);
|
||||
if (_isFrozen == false) return l;
|
||||
|
||||
l.Dispose();
|
||||
throw new InvalidOperationException("Resolution is frozen, it is not possible to configure it anymore.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,20 +79,23 @@ namespace Umbraco.Core.ObjectResolution
|
||||
// keep the class here because it needs write-access to Resolution.IsFrozen
|
||||
private class DirtyBackdoor : IDisposable
|
||||
{
|
||||
private static readonly System.Threading.ReaderWriterLockSlim _dirtyLock = new ReaderWriterLockSlim();
|
||||
|
||||
private IDisposable _lock;
|
||||
private bool _frozen;
|
||||
private readonly IDisposable _lock;
|
||||
private readonly bool _frozen;
|
||||
|
||||
public DirtyBackdoor()
|
||||
{
|
||||
_lock = new WriteLock(_dirtyLock);
|
||||
LogHelper.Debug(typeof(DirtyBackdoor), "Creating back door for resolution");
|
||||
|
||||
_lock = new WriteLock(ConfigurationLock);
|
||||
_frozen = _isFrozen;
|
||||
_isFrozen = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LogHelper.Debug(typeof(DirtyBackdoor), "Disposing back door for resolution");
|
||||
|
||||
_isFrozen = _frozen;
|
||||
_lock.Dispose();
|
||||
}
|
||||
@@ -96,11 +107,17 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <exception cref="InvalidOperationException">resolution is already frozen.</exception>
|
||||
public static void Freeze()
|
||||
{
|
||||
if (_isFrozen)
|
||||
throw new InvalidOperationException("Resolution is frozen. It is not possible to freeze it again.");
|
||||
LogHelper.Debug(typeof(Resolution), "Freezing resolution");
|
||||
|
||||
_isFrozen = true;
|
||||
if (Frozen != null)
|
||||
using (new WriteLock(ConfigurationLock))
|
||||
{
|
||||
if (_isFrozen)
|
||||
throw new InvalidOperationException("Resolution is frozen. It is not possible to freeze it again.");
|
||||
|
||||
_isFrozen = true;
|
||||
}
|
||||
|
||||
if (Frozen != null)
|
||||
Frozen(null, null);
|
||||
}
|
||||
|
||||
@@ -110,7 +127,20 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <remarks>To be used in unit tests.</remarks>
|
||||
internal static void Reset()
|
||||
{
|
||||
_isFrozen = false;
|
||||
LogHelper.Debug(typeof(Resolution), "Resetting resolution");
|
||||
|
||||
/*
|
||||
var trace = new System.Diagnostics.StackTrace();
|
||||
var testing = trace.GetFrames().Any(frame =>
|
||||
frame.GetMethod().DeclaringType.FullName.StartsWith("Umbraco.Tests"));
|
||||
if (testing == false)
|
||||
throw new InvalidOperationException("Only unit tests can reset configuration.");
|
||||
*/
|
||||
|
||||
using (new WriteLock(ConfigurationLock))
|
||||
{
|
||||
_isFrozen = false;
|
||||
}
|
||||
Frozen = null;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user