Compare commits

...

317 Commits

Author SHA1 Message Date
Stephan fed06c88e8 Version 7.6-alpha065 2017-02-17 14:28:26 +01:00
Stephan 5dec2861d9 Fix event name issue 2017-02-17 14:28:26 +01:00
Stephan 23b3bb37ee Scope - fix issue with EventMessages 2017-02-17 13:10:22 +01:00
Stephan c7783d1cd1 Merge pull request #1759 from umbraco/temp-deploy-214
deploy-214 Add a "pretty" name to the IEntity or lookup a name when one is not immediately available
2017-02-16 18:45:31 +01:00
Stephan 2104b109f2 Merge pull request #1746 from umbraco/temp-U4-9509
U4-9509 Fix bugs in shadow filesystem
2017-02-16 18:43:04 +01:00
Stephan 578ca7d41d Merge branch dev-v7.6 into temp-U4-9509 2017-02-16 18:38:22 +01:00
Claus 42c23da9f5 removing IPrettyArtifact and moving properties to IArtifact instead. 2017-02-16 12:54:42 +01:00
Claus 33d35fd9f7 bumping version just to be sure. 2017-02-16 10:01:37 +01:00
Claus cc885c1231 Merge pull request #1750 from umbraco/deploy-219
deploy-219 - files management
2017-02-16 09:52:59 +01:00
Shannon 422dd1a627 Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into dev-v7.6 2017-02-16 13:22:59 +11:00
Sebastiaan Janssen 759e444624 Merge pull request #1467 from ArnoldV/dev-v7
U4-8990 Updated Dutch Language File
2017-02-15 22:39:44 +01:00
Sebastiaan Janssen 2a7ef5de26 Merge pull request #1756 from umbraco/temp-U4-9512
U4-9512 Missing index on umbracoUser2NodePermission
2017-02-15 15:14:33 +01:00
Claus 43e892563d Merge branch 'aarym-u4-9491' into dev-v7.6 2017-02-15 14:17:43 +01:00
Shannon 4d1e64b279 U4-9512 Missing index on umbracoUser2NodePermission 2017-02-15 21:51:14 +11:00
Shannon 975c97cfb0 Doh! Changed update to Execute 2017-02-15 21:24:45 +11:00
Shannon 0c613347ae Updates ClearPublished logic to be done in a single SQL statement instead of several 2017-02-15 21:22:08 +11:00
Claus a5ca058cb2 adding another wildcard test for ShadowFileSystem. 2017-02-14 15:14:21 +01:00
Claus 88a1ad04aa ensuring filter is applied to the shadow filesystem when using GetFiles.
added tests for various filters.
2017-02-14 14:57:04 +01:00
Warren Buckley e619c402b4 Merge branch 'temp-U4-9310' into dev-v7.6
# Conflicts:
#	src/Umbraco.Core/UdiEntityType.cs
2017-02-14 11:53:38 +00:00
Warren Buckley c9294e5281 In the init DB creation the Media & Member Picker was inserted into the DB the wrong way round - now assigned with the correct name for the DataType 2017-02-14 11:37:42 +00:00
Stephan 0074f57970 Version 7.6-alpha063 2017-02-14 11:23:20 +01:00
Stephan 9b4a6f2761 Merge origin/dev-v7.6 into deploy-219 2017-02-14 11:22:03 +01:00
Stephan f889b5206c Scope - handle publishing events 2017-02-14 11:11:40 +01:00
Stephan 507d7dd4f0 Scope - forgot to complete some scopes 2017-02-14 11:11:40 +01:00
Stephan 55c5b41693 Scope - report completion 2017-02-14 11:11:39 +01:00
Stephan b58d8677b3 Scope - bugfixing 2017-02-14 11:11:39 +01:00
Shannon f30d7fc68c Fixes nuspec to be net45 as a min 2017-02-14 14:29:23 +11:00
aaron.morey f5a204d1de U4-9491 feedback to implement into core css, be more specific with class naming & with usage and to implement a lighter red color for appearance 2017-02-14 09:14:05 +11:00
Warren Buckley d504d8f54e Update Core Deploy EntityTypes for Forms (Workflow is part of main form artifact) and we dont deploy records. Changing to PreValue & DataSources 2017-02-13 15:54:39 +00:00
Claus b519d68e1a fixing PartialViewMacrosTree and ScriptTree in trees.Release.config. 2017-02-13 13:34:35 +01:00
Claus deb8d652c5 fixing PartialViewsTreeController reference in trees.Release.config. 2017-02-13 12:44:16 +01:00
Sebastiaan Janssen a68f4e284a Merge pull request #1752 from umbraco/temp-U4-9517
U4-9517 BackOfficeSignInManager doesn't automatically keep LastLoginD…
2017-02-13 10:38:41 +01:00
Shannon 90b43584d2 Oops, fixes issue with attemping to reduce code but the 'section' parameter isn't the same in the config 'type' so needs to be manually applied like it was before. Fixes MultipleMediaPickerPropertyEditor to be multiple by default (is a bug in current versions too) 2017-02-13 19:57:21 +11:00
Stephan 12dd01dd29 Scope - refactor call context 2017-02-13 09:05:33 +01:00
Stephan c64e3a8908 Scope - refactor event dispatching 2017-02-13 09:05:32 +01:00
Shannon d554417fde U4-9517 BackOfficeSignInManager doesn't automatically keep LastLoginDate set on users 2017-02-13 16:47:04 +11:00
Stephan 74e8390bda Scope - enable http vs call context switching 2017-02-11 09:56:15 +01:00
Stephan 4f9bbdf7f7 Scope - Fix BulkInsertRecords, ensure a db connection is open 2017-02-11 09:56:12 +01:00
Stephan 67fc8b6215 Scope - fix media files deletion 2017-02-11 09:55:32 +01:00
Sebastiaan Janssen 3840ac2fed Merge pull request #1743 from umbraco/temp-U4-9484
U4-9484 Implement correct tree menu logic for partialviews, partialviewmacros and scripts
2017-02-10 17:04:41 +01:00
Sebastiaan Janssen 7c64f1aa0d Merge pull request #1745 from umbraco/temp-U4-7385
Fixes: U4-7385 Improve UI for listview - Make it clearer that users can search children
2017-02-10 16:43:35 +01:00
Claus 771f8d92f2 adding in missing extension method stuff for some udis. 2017-02-10 14:44:56 +01:00
Stephan 82c18955b0 Version 7.6-alpha060 2017-02-10 14:38:50 +01:00
Claus eedcfc122b Merge pull request #1747 from umbraco/temp-U4-9513
U4-9513 Empty recycle bin SQL logic can SQL to timeout due to query i…
2017-02-10 11:42:14 +01:00
Claus 8cf3110708 fixing typo and missing file ref in csproj. 2017-02-10 11:41:46 +01:00
Mads Rasmussen 0dc0e68174 reset pagination when searching 2017-02-10 10:36:58 +01:00
Claus 5226762528 Merge branch 'temp-U4-9351' into dev-v7.6
Conflicts:
	src/Umbraco.Core/Events/ImportPackageEventArgs.cs
	src/Umbraco.Core/Umbraco.Core.csproj
	src/Umbraco.Web/Editors/PackageInstallController.cs
2017-02-10 10:08:31 +01:00
Claus 7889f82a9a added a check so the 'ensure' wont fail if the path is already as it should be.
added unit test for this method.
2017-02-10 09:25:16 +01:00
Emil Wangaa aaa9c8f194 Fixes delete of folders in partialviews,partialviewmacros and scripts 2017-02-10 09:24:14 +01:00
Sebastiaan Janssen 3d1a44b32c Merge branch 'temp-U4-8998' into dev-v7.6
# Conflicts:
#	src/Umbraco.Core/Umbraco.Core.csproj
2017-02-10 08:25:52 +01:00
Sebastiaan Janssen 724ec9ab73 Merge pull request #1748 from umbraco/temp-U4-8835
U4-8835 cmsMember.LoginName needs a DB index
2017-02-10 08:22:36 +01:00
Shannon aa0eaf590c adds indexes to relation type 2017-02-10 15:51:41 +11:00
Shannon 2b82520067 U4-8998 umbracoRelation table needs unique index on columns: parentId, childId, relType 2017-02-10 15:38:53 +11:00
Shannon 5aed6334c4 missing file 2017-02-10 14:24:33 +11:00
Shannon 91157caf0e U4-8835 cmsMember.LoginName needs a DB index 2017-02-10 14:23:46 +11:00
Shannon dbe489c0f2 U4-9513 Empty recycle bin SQL logic can SQL to timeout due to query inefficiencies 2017-02-10 12:27:50 +11:00
Shannon 2652a583e0 updates mediapicker.controller pre value to not make an uneeded request 2017-02-10 10:48:43 +11:00
Shannon bc1758f569 Updates the content picker to not make an unneeded request 2017-02-10 10:45:35 +11:00
Shannon a898c2d697 Fixes ParameterSwapControllerActionSelector when there is an empty parameter (not null but without a value) 2017-02-10 10:36:48 +11:00
Shannon e612bcae58 Merge branch 'refs/heads/dev-v7.6' into temp-U4-9310 2017-02-10 10:04:58 +11:00
Shannon d27fee8a5b Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into dev-v7.6 2017-02-10 09:38:03 +11:00
Sebastiaan Janssen 7aef8cbb67 Merge branch 'temp-U4-9481' into dev-v7.6
# Conflicts:
#	src/Umbraco.Core/Services/FileService.cs
2017-02-09 21:55:17 +01:00
Sebastiaan Janssen a7e28a97d9 Merge pull request #1715 from umbraco/temp-U4-9450
U4-9450 Paged data queries return all property data in the entire dat…
2017-02-09 21:02:15 +01:00
Stephan 308a61895e deploy-219 - implement filesystem CanAddPhysical, AddFile from physical path 2017-02-09 15:58:44 +01:00
Stephan 8e5c57eb60 U4-9322 - rename kabum in Scope.RobustExit 2017-02-09 15:58:17 +01:00
Claus 9c5526f281 adding test for GetUrl on shadow. 2017-02-09 15:00:38 +01:00
Claus 9c803cc234 more test and comments to existing tests. 2017-02-09 15:00:37 +01:00
Claus f24fd42a69 GetFullPath now uses ShadowFileSystem. 2017-02-09 15:00:37 +01:00
Claus 9b7f459c44 added more checks to shadow tests to ensure correct behavior. 2017-02-09 15:00:36 +01:00
Claus 273a5cef28 fixed bug in GetFiles with filter param.
test for shadow filesystem GetFiles.
2017-02-09 15:00:35 +01:00
Claus 57945fcec6 fixing broken build - referencing files that aren't committed. 2017-02-09 14:41:22 +01:00
Sebastiaan Janssen ade3c4ee6c Merge pull request #1732 from rsoeteman/U4-9493
U4 9493
2017-02-09 14:26:44 +01:00
Arnold Visser 7da7c1e5a1 Addes all new/missing fields since last edit
Checked against the English version
2017-02-09 13:47:31 +01:00
Sebastiaan Janssen 8fe904d780 Merge pull request #1744 from umbraco/temp-U4-9433
U4-9433 Add index to path column on umbracoNode
2017-02-09 13:43:44 +01:00
Mads Rasmussen 1901fbb6ed add markup for member picker and content picker 2017-02-09 10:42:15 +01:00
Mads Rasmussen 1de52482e3 fix back arrow alignment 2017-02-09 09:23:30 +01:00
Shannon 1c0d4001ab U4-9433 Add index to path column on umbracoNode 2017-02-09 17:27:23 +11:00
Shannon f04d70f2d9 Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into dev-v7.6 2017-02-09 17:04:41 +11:00
Shannon Deminick 55098deac0 Merge pull request #1728 from umbraco/temp-u4-9487
U4-9487 - optimize initialization of xml nodes
2017-02-09 16:56:05 +11:00
Shannon 58c5618d30 Adds benchmarks for XML perf testing - had to make the init xml code static with out params to test but that's ok. 2017-02-09 16:42:59 +11:00
Shannon d0cbeb4a0a Better optimized initialization of xml nodes with less allocations 2017-02-09 15:29:38 +11:00
Shannon ce0821e2d6 Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into dev-v7.6
# Conflicts:
#	build/NuSpecs/UmbracoCms.Core.nuspec
#	src/Umbraco.Tests/Umbraco.Tests.csproj
2017-02-09 13:20:33 +11:00
Shannon Deminick b6e9db7363 Merge pull request #1726 from umbraco/temp-u4-9322
Scope Scope Scope!
2017-02-09 12:30:58 +11:00
Shannon 9f710a957d Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-u4-9322
# Conflicts:
#	src/Umbraco.Core/Services/FileService.cs
2017-02-09 12:30:01 +11:00
Shannon b7578e2b98 fixes tests 2017-02-09 12:07:16 +11:00
Shannon 97e8973cbd Ensures that the RelateOnTrashHandler doesn't attempt to create a relationship for an item that doesn't exist 2017-02-09 11:40:13 +11:00
Shannon 2dc45c3b9f Tons of code duplication removed, streamlines the content, media and member repositories to use the same paged methods and filtering mechanism (much bettter), adds some tests, creates a marker interface for IContentServiceBase so we can use extension methods for now to de-duplicate some of the code between media and content services 2017-02-09 10:34:40 +11:00
Mads Rasmussen 49c2ae3673 cleanup 2017-02-08 23:37:48 +01:00
Mads Rasmussen 5f22c81994 fixes: U4-9467 Mini list view: don’t animate list view if it is a start node 2017-02-08 23:37:33 +01:00
Mads Rasmussen f13427f5fb fixes to breadcrumb 2017-02-08 22:49:16 +01:00
Mads Rasmussen 2785eaeda5 only show unpublished state for content 2017-02-08 20:22:54 +01:00
Sebastiaan Janssen bb31eb7c35 Emit only relative paths for files in a package 2017-02-08 16:19:47 +01:00
Emil Wangaa a31cfb86f8 Moved reload to be last menu option and added a separator for delete option to keep consistency 2017-02-08 15:57:53 +01:00
Shannon 4feb876463 Fixes issues of deleting a content item and the trashing events raised for content/media that is going to be deleted anyways, adds tests to support. Fixes MediaService to not delete the xml directly and instead rely on it's repository to do that (which is the correct way), streamlines both content/media service logic to be the same (need to start sharing code!) 2017-02-09 01:47:04 +11:00
Emil Wangaa e4802b592f Centralized the tree menu logic for partialviews, partialviewmacros and scripts to the FileSystemTreeController and implemented logic to match what we do for Document Types 2017-02-08 15:43:31 +01:00
Mikkel Holck Madsen a8914dcfdb Merge pull request #1740 from umbraco/temp-U4-9458
Adds unit tests for U4-9458 UmbracoExamine performance optimizations
2017-02-08 15:12:07 +01:00
Mads Rasmussen 548f9fd01f open mini list view before children are loaded 2017-02-08 15:02:03 +01:00
Mads Rasmussen 843796c9a4 refactor move logic from tree controller into to mini-list-view component 2017-02-08 14:58:54 +01:00
Sebastiaan Janssen 7648c76f6f List full path of each file installed 2017-02-08 14:45:07 +01:00
Sebastiaan Janssen 308ed3bba7 Removed files that are not found on disk don't get updated to list their full path 2017-02-08 14:44:48 +01:00
Sebastiaan Janssen 7bcb2b11f6 Makes the (un)install summary accessible to events 2017-02-08 14:43:59 +01:00
Shannon e06cda98e9 fixes SQL IN expression 2017-02-08 22:25:07 +11:00
Shannon fb02a5b06d Fixes issue of removing content types in bulk (like when removing a package) and having the trashed event being raised for what will end up being a deleted node, Fixes a bug with the (very silly) extension methods for content types, removes singleton accessors from these (very silly) extension methods - which actually never worked for media types! 2017-02-08 21:59:16 +11:00
Mads Rasmussen c060856782 fix breadcrumb for pickers with a startNode 2017-02-08 11:09:09 +01:00
Sebastiaan Janssen 311edd3279 Adds missing dependency (it's in dev-v7, must have been a merge where it was forgotten to be merged into dev-v7.6) 2017-02-08 10:36:36 +01:00
Claus 1c89f8c8dd Merge branch 'temp-U4-1324' into dev-v7.6 2017-02-08 09:54:12 +01:00
Mads Rasmussen 857063ebd8 fixes: U4-9465 Mini list view: don’t show expand arrow on list views in trees not supporting mini list view 2017-02-08 09:53:52 +01:00
Claus 24063c403a Merge remote-tracking branch 'origin/dev-v7.6' into temp-U4-1324
Conflicts:
	src/Umbraco.Core/packages.config
	src/Umbraco.Tests/packages.config
2017-02-08 09:14:27 +01:00
Claus 0f930d7342 Merge pull request #1741 from umbraco/temp-U4-9499
U4-9499 Update to latest Json.Net
2017-02-08 08:58:22 +01:00
Emil Wangaa c36000c4dd Adds create container endpoint for creation folders for scripts, partialviews, partialviewmacros 2017-02-08 08:52:36 +01:00
Mads Rasmussen 999de5ae1d add client side logic for partial view macros folder creation 2017-02-08 08:52:36 +01:00
Mads Rasmussen 00982d4278 add client side logic for partial views folder creation 2017-02-08 08:46:10 +01:00
Mads Rasmussen cda47ed536 add client side logic for scripts folder creation 2017-02-08 08:45:23 +01:00
Shannon f4f1f6774d U4-9499 Update to latest Json.Net 2017-02-08 17:47:32 +11:00
Shannon a20cc60c10 Updates new UDI pickers that have start nodes for pre-values to also store UDIs for their prevalues, this has a knock on effect in that we need to be able to inject some server side config for pre-value editors (otherwise we'd have to make diff udi prevalue editors but that isn't very re-usable), so have implemented that and then updated all of the logic to deal with the udi idType. 2017-02-08 16:15:27 +11:00
Shannon 846ac573cc Fixes tests, ensures we don't install test data that doesn't exist 2017-02-08 14:18:37 +11:00
Warren Buckley 92acc3d331 Merge pull request #1736 from umbraco/temp-U4-9429
U4-9429 Make new create dialogs for code files (or rethink snippets)
2017-02-07 21:28:43 +00:00
Warren Buckley ed4db61288 Merge pull request #1738 from umbraco/temp-U4-9314
U4-9314 Update to .Net Framework 4.6.2
2017-02-07 17:49:17 +00:00
Warren Buckley ba6705a706 Merge branch 'dev-v7.6' into temp-U4-9314 2017-02-07 17:27:07 +00:00
Mads Rasmussen dc980bd78e remove null 2017-02-07 16:41:01 +01:00
Mads Rasmussen c9f99d7f42 localise create items 2017-02-07 16:35:33 +01:00
Sebastiaan Janssen 73e219b23c Fix unit tests 2017-02-07 16:24:34 +01:00
Sebastiaan Janssen 07e0de5824 Adds unit tests for U4-9458 UmbracoExamine performance optimizations 2017-02-07 16:20:40 +01:00
Mads Rasmussen 79a103662f remove alerts 2017-02-07 16:09:36 +01:00
Mads Rasmussen 2ba5627e2c fixes: U4-9471 Mini list view: show breadcrumb instead of back button 2017-02-07 15:57:53 +01:00
Warren Buckley 122295c89f Sets MacroPicker aka MacroContainer as deperecated 2017-02-07 13:55:49 +00:00
Mads Rasmussen 2669c68df0 fixes: U4-9468 Mini list view: add empty state for no children + no search result 2017-02-07 11:55:45 +01:00
Sebastiaan Janssen 389b29599b U4-9495 ImageProcessor high CPU usage when trimCache=true 2017-02-07 10:29:11 +01:00
Mads Rasmussen 912a6a52cc fixes: U4-9472 Mini list view: show published/unpublished state of node 2017-02-07 10:10:37 +01:00
Claus 925c343a30 U4-9351 Add events for package install/uninstall 2017-02-07 10:00:34 +01:00
Shannon 351796fd6b Created test to verify problem with deleting content of content type in a loop (i.e. on package uninstall) 2017-02-07 15:46:50 +11:00
Shannon e58d9a9535 Fixing tests, fixes PublishingStrategy to hvae the scope provider passed in 2017-02-07 14:40:45 +11:00
Shannon 3fa7c4d7ef Removes POC class, fixes content service trash/move events so there's not ambiguous 2017-02-07 14:08:55 +11:00
Shannon b6b2a53361 Fixes publishing strategy to use the normal event messages collection since we haven't actually wired those up to the scope for now. 2017-02-07 14:07:01 +11:00
Shannon 4647ed2551 Merge branch 'refs/heads/dev-v7.6' into temp-u4-9322
# Conflicts:
#	src/Umbraco.Core/DatabaseContext.cs
#	src/Umbraco.Core/UmbracoApplicationBase.cs
2017-02-07 13:44:17 +11:00
Shannon e7e6ad6d0b Some adjustments for U4-9477 2017-02-07 13:36:32 +11:00
Shannon 0e727f91da Merge branch 'refs/heads/dev-v7.6' into temp-U4-9310
# Conflicts:
#	src/UmbracoExamine/UmbracoMemberIndexer.cs
2017-02-07 13:21:02 +11:00
Shannon a71ec8dd81 Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into dev-v7.6 2017-02-07 12:50:44 +11:00
Shannon f816a63b54 Ok, reverting the correct commit this time: Revert "Fixes database context issue during startup when there is no db configured" 2017-02-07 12:49:26 +11:00
Shannon 138991d951 OMG Re-rever "DOH! Revert "Fixes database context issue during startup when there is no db configured", i reverted the wrong commit on the wrong branch 2017-02-07 12:43:06 +11:00
Shannon 92ce064d30 DOH! Revert "Fixes database context issue during startup when there is no db configured" pushed to the wrong branch 2017-02-07 12:40:28 +11:00
Shannon ab3bfa4e92 Fixes database context issue during startup when there is no db configured 2017-02-07 12:39:35 +11:00
Shannon fa8f3985b8 Fixes database context issue during startup when there is no db configured 2017-02-07 12:33:37 +11:00
Shannon 179523224a Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9310 2017-02-07 12:15:54 +11:00
Shannon 19e4bee34e fixes test 2017-02-07 12:02:38 +11:00
Shannon f1709f6066 Fixes identity seed for db 2017-02-07 11:58:10 +11:00
Shannon f2be235f85 Fixes the db data type for UDI media picker, fixes the initial db installation data to use the new pickers. 2017-02-07 11:44:04 +11:00
Mikkel Holck Madsen 175c785705 Merge pull request #1722 from umbraco/temp-U4-9458
Changes IndexFieldPolicies to be a StaticFieldCollection which is a k…
2017-02-06 21:45:54 +01:00
Mikkel Holck Madsen 72aebd3ecf Merge branch 'dev-v7' into temp-U4-9458 2017-02-06 21:44:26 +01:00
Warren Buckley c8408760df Merge pull request #1737 from umbraco/temp-U4-9474
Fixes: U4-9474 Update macro picker to follow the new picker UI
2017-02-06 16:03:25 +00:00
Warren Buckley 5834649abc Merge pull request #1733 from umbraco/temp-U4-9466
fixes: U4-9466 Duplicate reorder links for Umbraco grid
2017-02-06 15:36:04 +00:00
Mads Rasmussen 2eda70542f rename macro container to macro picker 2017-02-06 16:16:26 +01:00
Mads Rasmussen a2a17faca1 Fixes: U4-9474 Update macro picker to follow the new picker UI 2017-02-06 16:11:51 +01:00
Shannon a6c2844d12 updates nuspec with correctly targeted framework 2017-02-07 01:03:50 +11:00
Mikkel Holck Madsen 802cbb82c4 Merge pull request #1723 from umbraco/temp-U4-9448
U4-9448 Slave Front End server requires write access to database when…
2017-02-06 15:01:30 +01:00
Shannon 6f16b98b32 regression issue from refactoring how media works with Deploy 2017-02-07 00:57:44 +11:00
Mikkel Holck Madsen 42f5507fe8 Merge pull request #1734 from umbraco/temp-U4-9492
U4-9492 Update to latest ImageProcessor
2017-02-06 14:39:01 +01:00
Shannon 72910cb35e Updates everything to .Net 4.6.2, had to update the semver reference too - no breaking changes though 2017-02-07 00:22:34 +11:00
Shannon 7be30ad6ac Moves all binding redirects via code to one place 2017-02-06 23:46:44 +11:00
Sebastiaan Janssen 5a8e37f5b4 U4-9492 Update to latest ImageProcessor 2017-02-06 13:46:10 +01:00
Shannon f87ccc0a53 Updates nuspec 2017-02-06 23:36:56 +11:00
Shannon 40ed77103f Updates to latest 1.x version of log4net (before the real 2.x version) with binding redirects and codebase binding redirects to redirect from non-signed to signed version 2017-02-06 23:07:20 +11:00
Mads Rasmussen 483762a984 fixes: U4-9466 Duplicate reorder links for Umbraco grid 2017-02-06 12:50:34 +01:00
Richard Soeteman a3c7b1fcfe Fixed issue U4-9494 2017-02-06 11:51:44 +01:00
Stephan 145ca54dcf Merge origin/dev-v7.6 into temp-u4-9322 2017-02-06 11:29:04 +01:00
Richard Soeteman 02785eea42 Fixed issue U4-9493 2017-02-06 11:26:58 +01:00
Stephan 1aecb66167 U4-9322 - restore DatabaseContext ctors, see U4-9477 2017-02-06 11:17:24 +01:00
Stephan 96244f9f7b U4-9322 - pushed DEBUG_SCOPES by mistake 2017-02-06 10:43:39 +01:00
Stephan e0cc59d212 Merge branch 'temp-u4-9322' of https://github.com/umbraco/Umbraco-CMS into temp-u4-9322 2017-02-06 10:09:31 +01:00
Stephan dd2946419b U4-9322 - fix broken DEBUG_SCOPES 2017-02-06 10:08:29 +01:00
Shannon d0ea8e83ad Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into dev-v7.6 2017-02-06 19:49:00 +11:00
Shannon Deminick 3c7d3b2f63 Merge pull request #1724 from umbraco/temp-installer-url-update
Update NewInstallStep.cs
2017-02-06 19:42:23 +11:00
Shannon a65ad3c72a Adds equality members for all event args, updates equality check for EventDefinitionBase to include the args (since that is quite important!), creates an OrderedHashSet class which can deduplicate lists for us based on member equality and supports first in or last in + tests. 2017-02-06 18:46:23 +11:00
Shannon f6a7b25e87 Updates CacheRefresherEventHandler to have consistent events, adds logic to find the event based on definitions, adds tests to test them all 2017-02-06 17:26:06 +11:00
aaron.morey d97cc36c53 u4-9491 Added an asterisk with appropriate css class to the property editor next to the label in the back office to indicate to the user that the field is mandatory before saving. This is more intuitive to the user to see what fields need filling in before submitting. This was implemented in our solution which is used by hundreds of back end users. 2017-02-06 17:24:53 +11:00
Shannon 0e06245177 Adds notes for stephen to look at 2017-02-06 14:46:02 +11:00
Stephan 5a81a9eed7 U4-9322 - various fixes 2017-02-04 15:44:56 +01:00
Stephan 38e7d7e697 Merge remote-tracking branch 'origin/temp-u4-9322' into temp-u4-9322 2017-02-03 21:54:55 +01:00
Stephan 820069d5d5 U4-9322 - filesystems & cleanup 2017-02-03 20:01:43 +01:00
Stephan 49f3979d18 U4-9487 - optimize initialization of xml nodes 2017-02-03 19:28:43 +01:00
Emil Wangaa 055e209e0b Fixed the path and virtualpath when creating and scaffolding codefiles
Ensuring that javascript files are created with the .js extension
2017-02-03 12:06:49 +01:00
Shannon 5579563105 Creates a test (which fails) so that stephen can have a look 2017-02-03 14:56:08 +11:00
Shannon cda9d0f9f2 pretty big perf boost so that we aren't going to the services user cache everytime we want to know the user's culture, this will already be set on the thread! 2017-02-03 14:42:06 +11:00
Shannon 49b7f15e3d fixes tests 2017-02-03 13:32:22 +11:00
Shannon 8ae700eedf code formatting and merge fixing 2017-02-03 12:23:32 +11:00
Shannon 411bbcc192 Merge branch 'refs/heads/dev-v7.6' into temp-u4-9322
# Conflicts:
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Core/Services/ContentService.cs
#	src/Umbraco.Core/Services/ContentTypeService.cs
#	src/Umbraco.Core/Services/DataTypeService.cs
#	src/Umbraco.Core/Services/MacroService.cs
#	src/Umbraco.Core/Services/MediaService.cs
#	src/Umbraco.Core/Services/MemberService.cs
#	src/Umbraco.Core/Services/MemberTypeService.cs
#	src/Umbraco.Core/Services/UserService.cs
#	src/Umbraco.Web/Umbraco.Web.csproj
2017-02-03 12:14:38 +11:00
Shannon d598fdce54 Updates to latest Examine release 2017-02-03 12:03:56 +11:00
Shannon db86f6e1ee Fixes up issues reported on review and adds a null check 2017-02-03 11:49:22 +11:00
Shannon c16ce4ce1f Merge branch 'dev-v7.6' of https://github.com/umbraco/Umbraco-CMS into dev-v7.6 2017-02-03 10:21:10 +11:00
Shannon 86021c5052 Adds remaining core methods to make 2FA providers work if you know how to wire it up 2017-02-03 00:47:28 +11:00
Mads Rasmussen de87701c95 remove option to create xml file 2017-02-02 14:46:23 +01:00
Shannon a03e0dfb53 Fixes tests 2017-02-02 22:21:36 +11:00
Shannon 5060e709d1 Fixed merge conflicts, adds required methods to auth controllers. 2017-02-02 22:11:34 +11:00
Emil Wangaa c073191f69 Fixes creation of codefiles in folders 2017-02-02 11:47:46 +01:00
Mads Rasmussen b4450ca7f2 add null check for startNode 2017-02-02 11:44:13 +01:00
Mads Rasmussen 7004765acf remove unused code 2017-02-02 11:40:53 +01:00
Mads Rasmussen 77a91bc017 fix entity look up 2017-02-02 11:28:56 +01:00
Mads Rasmussen 14b96da588 combine redundant resource code into one function 2017-02-02 11:19:56 +01:00
Emil Wangaa c21837e906 Merge remote-tracking branch 'origin/dev-v7.6' into temp-U4-9429
# Conflicts:
#	src/Umbraco.Web/Editors/CodeFileController.cs
2017-02-02 10:24:42 +01:00
Mads Rasmussen 99324035e5 clean up animation classes 2017-02-02 10:19:54 +01:00
Mads Rasmussen 8b9a5000be move back button styles to class 2017-02-02 10:09:31 +01:00
hartvig 32892cf5e0 Update NewInstallStep.cs 2017-02-02 10:05:14 +01:00
Mads Rasmussen c82bddb7dd remove fake list view search node 2017-02-02 09:51:13 +01:00
Stephan 478ca30948 U4-9322 fs - create scope option 2017-02-02 09:49:23 +01:00
Mads Rasmussen f2fd45576b speed up animations 2017-02-02 09:35:01 +01:00
Emil Wangaa 6bf5f4dbdf Fixed up tree menus for scripts, partialviews and partialviewmacros 2017-02-02 09:20:42 +01:00
Shannon 32d757b13c Updates the ContentRepository to not fetch the published version info in the same query when returning a collection of documents, instead we fetch the published version information in a separate query 2017-02-02 17:05:33 +11:00
Shannon 882d5ec7f5 missing null check 2017-02-02 16:02:29 +11:00
Shannon ce69497a68 Update code to have zero breaking changes for old public APIs 2017-02-02 14:16:39 +11:00
Shannon 1d43cf491b U4-9448 Slave Front End server requires write access to database when master performs unpublish 2017-02-02 14:09:49 +11:00
Mads Rasmussen d5a687cf3d start on code clean up - make ump-mini-list-view directive + move inline styles to classes 2017-02-01 23:12:49 +01:00
Emil Wangaa c9082ce511 Changed PostSave method in CodeFileController to create or update instead of only updating - this is for keeping consistency with how other new editors work 2017-02-01 15:46:22 +01:00
Claus 46c7737c17 Merge pull request #1714 from umbraco/temp-u4-9441
Fixes tree syncing issues
2017-02-01 14:04:18 +01:00
Claus 06c829eb27 initializing stringbuilder with text instead of appending after init.
trimming end instead of both beginning and end, since we know whats in the beginning.
2017-02-01 14:03:03 +01:00
Mads Rasmussen e439302b93 add support for getScaffold for scripts, partialViews and partialViewMacros editor 2017-02-01 13:54:16 +01:00
Emil Wangaa 6a1d7d2586 Adds GetScaffold in CodeFileController to simplify initial view in backoffice
Refactored the FileService a bit to be able to get content of the snippets without duplicate code
2017-02-01 12:33:20 +01:00
Shannon 27283754c0 Changes IndexFieldPolicies to be a StaticFieldCollection which is a keyed collection instead of using Linq and iterating to find the values in critical code 2017-02-01 16:48:09 +11:00
Shannon ff6ea0b42e Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9310
# Conflicts:
#	src/Umbraco.Core/Models/UmbracoObjectTypes.cs
2017-02-01 15:23:29 +11:00
Shannon 384a31e3c6 Gets member picker working with UDI, ensures that the GUID is indexed for all things - can't believe this wasn't being done, updates the search model mapper to return the UDI 2017-02-01 15:19:52 +11:00
Shannon 69b913c608 Gets MNTP working with UDI - updates EntityController to support all types of Ids for GetById 2017-02-01 14:07:13 +11:00
Shannon 83757c60b5 Adds UDI support for the content picker 2017-02-01 13:38:42 +11:00
Shannon 5b2212829c Small performance change which makes a fairly large impact when rebuilding very large indexes. 2017-02-01 13:02:22 +11:00
Mads Rasmussen 26acecb720 start on animation and nested list views 2017-02-01 00:28:40 +01:00
Stephan 4a6844b949 U4-9322 - repository caches, cleanup 2017-01-31 19:36:55 +01:00
Stephan ac9b484d3d U4-9322 - wire EventMessages, cleanup 2017-01-31 18:19:03 +01:00
Stephan 97a76d5ad4 U4-9322 - introduce ScopeContext that lives longer than Scope, now Xml does not multi-clone! 2017-01-31 16:42:01 +01:00
Mads Rasmussen d4fab66411 add UI for partial views create dialog 2017-01-31 15:00:37 +01:00
Mads Rasmussen 4cd287616e add UI for partial view macros create dialog 2017-01-31 14:44:52 +01:00
Mads Rasmussen d22afe07db add UI for scripts create dialog 2017-01-31 12:40:56 +01:00
Morten Christensen fa56f1a59d Merge branch 'dev-v7' into dev-v7.6 2017-01-31 12:27:40 +01:00
Morten Christensen 546209ddb5 Merge pull request #1716 from umbraco/temp-U4-9451
U4-9451 Package installation custom user control is not displayed aft…
2017-01-31 12:24:52 +01:00
Morten Christensen 1e7c44ead0 Merge remote-tracking branch 'origin/temp-U4-9451' into dev-v7.6 2017-01-31 11:38:41 +01:00
Per Ploug 90448f1c78 Added comments and made helper internal 2017-01-31 11:06:14 +01:00
Stephan 6dc34e1167 U4-9322 - cleanup EventExtensions 2017-01-31 10:37:37 +01:00
Warren Buckley 6956e01861 Merge pull request #1713 from umbraco/temp-U4-9413
U4-9413 moving links to document and media to top of generic properties.
2017-01-31 09:37:34 +00:00
Mads Rasmussen 7c42cd26c2 clear minilistview data when leaving 2017-01-31 10:26:03 +01:00
Mads Rasmussen 13758418ac add better load indicators 2017-01-31 10:14:17 +01:00
Emil Wangaa 6bd1a77b96 Simple wiring up of the create menu for partialview, partialviewmacro and script 2017-01-31 10:10:05 +01:00
Mads Rasmussen 77b04d74d4 load mini list view for pickers with a start node 2017-01-31 10:00:44 +01:00
Shannon e95cb14d48 Gets FromJsonPath working for EntityController to have GetByIds with all types of Ids for both GET and POST 2017-01-31 19:11:05 +11:00
Shannon 86b2dac231 Needed to change the 2nd query to use a new PagingSqlQuery object due to the way that < SQL 2012 formats it's paging query which is just not compatible with how we were parsing the queries for properties... i don't actually think it was working for a long tme. 2017-01-31 17:20:44 +11:00
Shannon 54bda6f12a U4-9451 Package installation custom user control is not displayed after a package is installed 2017-01-31 15:40:02 +11:00
Shannon 2f67a485aa Ensures that the sqlIds query is used for paging when processing members/media 2017-01-31 14:52:48 +11:00
Shannon f192f241cf U4-9450 Paged data queries return all property data in the entire database, not just for the paged subset 2017-01-31 13:48:47 +11:00
Mads Rasmussen bf797bccc7 open list view on expand 2017-01-30 22:35:19 +01:00
Mads Rasmussen 4336f5201d show expand arrow for list views 2017-01-30 22:34:04 +01:00
Emil Wangaa 431f91b900 Adds an api endpoint for getting the available snippets in the Umbraco folder 2017-01-30 14:57:08 +01:00
Mads Rasmussen ed019a90e2 start on mini list view for pickers 2017-01-30 14:25:10 +01:00
Stephan d4acc2c79b U4-9322 - EventsDispatchMode, fix tests 2017-01-30 13:53:31 +01:00
Per Ploug b05c4711ce Fixes tree syncing issues 2017-01-30 13:33:05 +01:00
Claus 74c167ed03 U4-9413 moving links to document and media to top of generic properties. 2017-01-30 13:18:30 +01:00
Shannon 8d598cd37c Gets EntityController working with UDIs, INT and GUID for GetByIds 2017-01-30 20:40:45 +11:00
Sebastiaan Janssen 690af8e494 Merge pull request #1711 from umbraco/temp-U4-9444
U4-9444 SessionState is not available when rendering macro contents i…
2017-01-30 10:00:40 +01:00
Shannon 30c2f95844 Merge branch 'refs/heads/dev-v7.6' into temp-U4-9310 2017-01-30 16:55:38 +11:00
Shannon 0078a5d8cc Getting the media picker wired up for udi 2017-01-30 16:54:01 +11:00
Shannon 09085e3bcf Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9310 2017-01-30 16:52:42 +11:00
Shannon a31b03b3e2 Lazily loads all known UdiType's based on reading through the known constants 2017-01-30 15:59:42 +11:00
Shannon 9d06ce53a4 U4-9444 SessionState is not available when rendering macro contents in the Rich Text Editor 2017-01-30 13:22:47 +11:00
Stephan d0e8e77fc5 Fix FileSystemProviderManagerTests 2017-01-28 10:05:19 +01:00
Stephan 798105998e U4-9322 - begin refactoring and fixing events 2017-01-27 18:17:21 +01:00
Shannon 9ef53536bf Updates the base editor models to include a readonly UDI, updates all model mappers to map the UDI, 2017-01-28 01:14:24 +11:00
Shannon 547351a719 Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9310 2017-01-28 00:25:25 +11:00
Shannon 371c4ae1a3 Starts creating new UDI picker property editors 2017-01-28 00:22:12 +11:00
Shannon 060137c798 Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9310 2017-01-27 23:07:48 +11:00
Stephan f4994a5133 Merge temp-u4-9322 (Shan Events) into temp-u4-9322a (Steph) - builds 2017-01-27 11:42:54 +01:00
Stephan 6bbfb65100 Merge temp-u4-9322 (Shan Events) into temp-u4-9322a (Steph) 2017-01-26 18:30:00 +01:00
Stephan cca3b9032f U4-9322 - cleanup 2017-01-25 14:18:33 +01:00
Stephan 4fc3398e49 U4-9322 - refactor UnitOfWork pattern 2017-01-25 13:34:41 +01:00
Shannon 2d5254e169 Makes ScopedEventManager aware of it's outer manager (if any) so it knows when to raise events (if configured) 2017-01-25 20:17:39 +11:00
Shannon c5033710ca Updates all ambigous event raising when the event finder cannot determine the name based on the args, all tests pass apart from 3 now 2017-01-25 18:44:29 +11:00
Shannon c824d27728 Updates remaining services to use the event manager to emit events correctly 2017-01-25 16:52:32 +11:00
Shannon ac54affa57 Refactor IPublishingStrategy, create IPublishingStrategy2 as a work around, adds EventMessages to IScopeUnitOfWork, ensures all events raised with IPublishingStrategy are done with uow 2017-01-25 16:49:44 +11:00
Shannon f7d7a43720 Fixes BulkInsertRecords to not require a trans , removes inner trans from PermissionRepository, fixes up events in PermissionRepository and MemberGroupRepository, fixes up uow in UserService 2017-01-25 15:15:28 +11:00
Shannon 6028907a02 Fixes up relation service with correct even emitting and no nested uow 2017-01-25 14:45:49 +11:00
Shannon e14ca4f3d0 Fixes up memberservice service with correct even emitting and no nested uow and removes inner transactions in MemberGroupRepository 2017-01-25 14:40:24 +11:00
Stephan a97b566065 U4-9422 - complete implementing scoped xml cache 2017-01-24 07:59:46 +01:00
Stephan 26a95a8ca1 U4-9322 - cont. implementing scoped xml cache 2017-01-23 18:03:38 +01:00
Stephan e3308e86f0 U4-9322 - begin implementing scoped xml cache 2017-01-23 18:03:08 +01:00
Stephan 1d98507785 TEMP 2017-01-23 15:39:46 +01:00
Shannon 096c3aeec6 started fixing up MemberService but this one is odd.... 2017-01-23 23:56:39 +11:00
Shannon 7ca5c48ae7 Fixes up membergroup service with correct even emitting and no nested uow 2017-01-23 23:30:47 +11:00
Shannon 4a1a216b3c Fixes up macro service with correct even emitting and no nested uow 2017-01-23 23:28:50 +11:00
Shannon f96d6aed9d Fixes up localization service with correct even emitting and no nested uow 2017-01-23 23:25:39 +11:00
Shannon 192aae71a2 Fixes up file service with correct even emitting and no nested uow and fixes up strange thing with RepositoryFactory and stylesheets 2017-01-23 23:21:53 +11:00
Shannon bd8af539b4 Fixes up data type service with correct even emitting and no nested uow 2017-01-23 22:55:16 +11:00
Shannon 479d859a75 Fixes up member type service with correct even emitting and no nested uow 2017-01-23 22:48:52 +11:00
Shannon 1050af5376 Updates ContentTypeService with the correct event emitting and no nested uows 2017-01-23 22:45:45 +11:00
Shannon 82dbfb769e Updated media service with correct usage of emitting events and having non nested uow 2017-01-23 22:29:38 +11:00
Shannon 55eb996c19 Adds a couple needed hack methods, updates ContentService to have the proper event emmitting without any nested uow's and ensuring that readonly uow's are committed. 2017-01-23 22:16:27 +11:00
Shannon ac4de99f30 Initial commit of changing all IsRaisedEventCancelled to use the event manager and inside of a uow 2017-01-23 20:48:51 +11:00
Stephan a3150b7d4e U4-9322 - missing files 2017-01-20 18:33:21 +01:00
Stephan 1701eb2450 u4-9322 - events cleanup 2017-01-20 17:05:22 +01:00
Stephan 20ac21aef2 Merge origin/temp-U4-9322 into temp-u4-9322 2017-01-20 16:51:25 +01:00
Stephan 4ac20de1dd U4-9322 - complete scope repository caches 2017-01-20 16:29:13 +01:00
Stephan 848eaf5e27 U4-9322 - cont. implementing scoped repository caches 2017-01-20 12:08:55 +01:00
Shannon a6996922b7 Adds more support for auto-finding event names, adds tests to support with examples of how to interrogate queued events 2017-01-20 17:45:46 +11:00
Stephan 434703f4ec U4-9322 - begin implementing scoped repository caches 2017-01-19 19:47:09 +01:00
Shannon b51e571803 Adds unit tests and fixes others 2017-01-19 18:25:45 +11:00
Shannon a275567dba Adds equality members to the EventDefinitionBase so they are tracked by their unique id, makes IEventManager disposable and disposes it on scope end. 2017-01-19 17:34:15 +11:00
Shannon 2a95ed3c7a no need for explicit implementation for this since we changed the old one 2017-01-19 17:29:36 +11:00
Shannon 9965aa461a Updates ContentService to raise events all with the EventManager 2017-01-19 17:18:06 +11:00
Shannon 8de8b53039 missing interface implementation 2017-01-19 16:58:10 +11:00
Shannon 27eeb383b4 Updates all repos to use IScopeUnitOfWork 2017-01-19 16:51:39 +11:00
Shannon 4f5347fa4d Creates IEventManager, IEventDefinition uses explicit implementation and 'new' keyword to force already existing uow providers to be IScopeUnitOfWork, creates extensions for the event manager so there's less work to do to wire it all up 2017-01-19 16:31:59 +11:00
Shannon 994f91e496 Creates IScopeUnitOfWork and IScopeUnitOfWorkProvider as internal along with ScopeRepositoryService which all services inherit from, this is maintaining backwards compat too 2017-01-19 15:37:17 +11:00
Stephan 4ab7f768fa U4-9322 - backport repository cache policy changes from v8 2017-01-18 18:48:13 +01:00
Stephan a020a02b26 U4-9322 - more fixes 2017-01-18 17:44:16 +01:00
Stephan ca777b26de U4-9322 - manage isolation levels, cleanup tests 2017-01-18 14:53:24 +01:00
Stephan 1f636d05d1 U4-9322 - fix scope leaks 2017-01-18 10:36:11 +01:00
Stephan 7495cd3f2c U4-9322 - cleanup 2017-01-17 16:46:34 +01:00
Stephan 5f68f26d2f U4-9322 - scope and database, getting tests to run 2017-01-17 16:32:40 +01:00
Shannon a5af5ba1a9 Adds new property to PropertyEditor's to mark them as deprecated so they don't show up in the data type list unless they are already selected 2017-01-17 18:54:50 +11:00
Stephan 1261f6b159 U4-9322 - getting tests to run 2017-01-16 19:54:09 +01:00
Stephan 5446e4b0fc U4-9322 - IScopeProviderInternal 2017-01-12 19:32:44 +01:00
Stephan 79c2400d27 U4-9322 - scope 2017-01-12 18:28:25 +01:00
Arnold Visser ca1f43d048 Merge pull request #1 from ArnoldV/Dutch-Language-Update
Dutch Languagefile Update
2016-09-06 10:53:49 +02:00
Arnold Visser e962a48138 Dutch Languagefile Update
This updates the Dutch language file with new/missing translations and fixes some wrong translations used by the grid.
2016-09-06 10:53:09 +02:00
477 changed files with 18864 additions and 7212 deletions
+41 -40
View File
@@ -15,6 +15,8 @@
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<!--Must be this way because 2.0.5 is actually 1.2.15.0 and 2.0.6 is actually 2.0.6 !-->
<dependency id="log4net" version="[2.0.5,2.0.6)" />
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.1, 3.0.0)" />
@@ -28,52 +30,51 @@
<dependency id="MySql.Data" version="[6.9.8, 7.0.0)" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Examine" version="[0.1.80, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.1, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.0, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 2.0.0)" />
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
<dependency id="Examine" version="[0.1.81, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.2, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.2, 5.0.0)" />
<dependency id="semver" version="[2.0.4, 3.0.0)" />
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.6.0, 5.0.0)" />
</dependencies>
</metadata>
<files>
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\businesslogic.dll" />
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\businesslogic.xml" />
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\cms.dll" />
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\cms.xml" />
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\controls.dll" />
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\controls.xml" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\interfaces.dll" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\interfaces.xml" />
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\log4net.dll" />
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\Microsoft.ApplicationBlocks.Data.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\SQLCE4Umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\SQLCE4Umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\System.Data.SqlServerCe.dll" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\System.Data.SqlServerCe.Entity.dll" />
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\TidyNet.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\Umbraco.Core.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\umbraco.DataLayer.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\umbraco.DataLayer.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\umbraco.editorControls.dll" />
<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.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" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\Umbraco.Web.UI.xml" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\UmbracoExamine.xml" />
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\net45\businesslogic.dll" />
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\net45\businesslogic.xml" />
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\net45\cms.dll" />
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\net45\cms.xml" />
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\net45\controls.dll" />
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\net45\controls.xml" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\net45\interfaces.dll" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\net45\interfaces.xml" />
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\net45\log4net.dll" />
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net45\Microsoft.ApplicationBlocks.Data.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net45\SQLCE4Umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net45\SQLCE4Umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net45\System.Data.SqlServerCe.dll" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net45\System.Data.SqlServerCe.Entity.dll" />
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\net45\TidyNet.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\net45\Umbraco.Core.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\net45\Umbraco.Core.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\net45\umbraco.DataLayer.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\net45\umbraco.DataLayer.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\net45\umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\net45\umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\net45\umbraco.editorControls.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\net45\umbraco.editorControls.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net45\umbraco.MacroEngines.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net45\umbraco.MacroEngines.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\net45\umbraco.providers.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\net45\umbraco.providers.xml" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net45\Umbraco.Web.UI.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net45\Umbraco.Web.UI.xml" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\net45\UmbracoExamine.dll" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\net45\UmbracoExamine.xml" />
<file src="tools\install.core.ps1" target="tools\install.ps1" />
<!-- Added to be able to produce a symbols package -->
@@ -104,4 +105,4 @@
<file src="..\_BuildOutput\bin\UmbracoExamine.pdb" target="lib" />
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
</files>
</package>
</package>
+3 -1
View File
@@ -16,9 +16,10 @@
<tags>umbraco</tags>
<dependencies>
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.5, 4.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
</dependencies>
</metadata>
<files>
@@ -45,6 +46,7 @@
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
<file src="tools\processing.config.install.xdt" target="Content\Config\imageprocessor\processing.config.install.xdt" />
<file src="tools\cache.config.install.xdt" target="Content\Config\imageprocessor\cache.config.install.xdt" />
<file src="build\**" target="build" />
</files>
</package>
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<caching xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<caches>
<cache name="DiskCache" trimCache="false" xdt:Transform="SetAttributes(trimCache)" xdt:Locator="Match(name)" />
</caches>
</caching>
+1 -1
View File
@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.0
alpha056
alpha065
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SQLCE4Umbraco</RootNamespace>
<AssemblyName>SQLCE4Umbraco</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>
</SccProjectName>
+1 -1
View File
@@ -240,7 +240,7 @@ namespace SqlCE4Umbraco
{
var cmd = trx == null ? new SqlCeCommand(commandText, conn) : new SqlCeCommand(commandText, conn, trx);
AttachParameters(cmd, commandParameters);
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
return cmd.ExecuteReader();
}
catch
{
+2 -2
View File
@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -32,4 +32,4 @@
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" /></startup></configuration>
+1 -1
View File
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.0")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha056")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha065")]
+12 -1
View File
@@ -7,6 +7,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Profiling;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
@@ -162,6 +163,9 @@ namespace Umbraco.Core
/// </summary>
public static ApplicationContext Current { get; internal set; }
// fixme
internal IScopeProvider ScopeProvider { get { return _databaseContext == null ? null : _databaseContext.ScopeProvider; } }
/// <summary>
/// Returns the application wide cache accessor
/// </summary>
@@ -418,10 +422,17 @@ namespace Umbraco.Core
this.ApplicationCache = null;
if (_databaseContext != null) //need to check the internal field here
{
if (_databaseContext.ScopeProvider.AmbientScope != null)
{
var scope = _databaseContext.ScopeProvider.AmbientScope;
scope.Dispose();
}
/*
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.Database != null)
{
DatabaseContext.Database.Dispose();
}
}
*/
}
this.DatabaseContext = null;
this.Services = null;
+48
View File
@@ -0,0 +1,48 @@
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using Umbraco.Core;
namespace Umbraco.Core
{
/// <summary>
/// Manages any assembly binding redirects that cannot be done via config
/// </summary>
internal class BindingRedirects
{
public static void Initialize()
{
// this only gets called when an assembly can't be resolved
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=null", RegexOptions.Compiled);
private const string Log4NetReplacement = "log4net, Version=1.2.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a";
/// <summary>
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//log4net:
// Use regex to match and replace
if (Log4NetAssemblyPattern.IsMatch(args.Name) && args.Name != Log4NetReplacement)
{
return Assembly.Load(Log4NetAssemblyPattern.Replace(args.Name, Log4NetReplacement));
}
//AutoMapper:
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null"))
return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005"));
return null;
}
}
}
@@ -1,23 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
/// <summary>
/// The default cache policy for retrieving a single entity
/// Represents the default cache policy.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TId">The type of the identifier.</typeparam>
/// <remarks>
/// This cache policy uses sliding expiration and caches instances for 5 minutes. However if allow zero count is true, then we use the
/// default policy with no expiry.
/// <para>The default cache policy caches entities with a 5 minutes sliding expiration.</para>
/// <para>Each entity is cached individually.</para>
/// <para>If options.GetAllCacheAllowZeroCount then a 'zero-count' array is cached when GetAll finds nothing.</para>
/// <para>If options.GetAllCacheValidateCount then we check against the db when getting many entities.</para>
/// </remarks>
internal class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
private readonly RepositoryCachePolicyOptions _options;
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
@@ -27,242 +30,223 @@ namespace Umbraco.Core.Cache
_options = options;
}
protected string GetCacheIdKey(object id)
public override IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
if (id == null) throw new ArgumentNullException("id");
return string.Format("{0}{1}", GetCacheTypeKey(), id);
return new ScopedRepositoryCachePolicy<TEntity, TId>(this, runtimeCache, scope);
}
protected string GetCacheTypeKey()
protected string GetEntityCacheKey(object id)
{
if (id == null) throw new ArgumentNullException("id");
return GetEntityTypeCacheKey() + id;
}
protected string GetEntityTypeCacheKey()
{
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
}
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
protected virtual void InsertEntity(string cacheKey, TEntity entity)
{
Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
}
protected virtual void InsertEntities(TId[] ids, TEntity[] entities)
{
if (ids.Length == 0 && entities.Length == 0 && _options.GetAllCacheAllowZeroCount)
{
// getting all of them, and finding nothing.
// if we can cache a zero count, cache an empty array,
// for as long as the cache is not cleared (no expiration)
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities);
}
else
{
// individually cache each item
foreach (var entity in entities)
{
var capture = entity;
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
}
}
}
/// <inheritdoc />
public override void Create(TEntity entity, Action<TEntity> persistNew)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
try
{
persistMethod(entity);
persistNew(entity);
//set the disposal action
SetCacheAction(() =>
// just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
//just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity,
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetCacheTypeKey());
});
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
}
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
catch
{
//set the disposal action
SetCacheAction(() =>
{
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
Cache.ClearCacheItem(GetCacheIdKey(entity.Id));
// if an exception is thrown we need to remove the entry from cache,
// this is ONLY a work around because of the way
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetEntityTypeCacheKey());
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetCacheTypeKey());
});
throw;
}
}
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
/// <inheritdoc />
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
try
{
persistMethod(entity);
}
finally
{
//set the disposal action
var cacheKey = GetCacheIdKey(entity.Id);
SetCacheAction(() =>
persistUpdated(entity);
// just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.ClearCacheItem(cacheKey);
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetCacheTypeKey());
});
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
}
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
catch
{
// if an exception is thrown we need to remove the entry from cache,
// this is ONLY a work around because of the way
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetEntityTypeCacheKey());
throw;
}
}
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
/// <inheritdoc />
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
if (entity == null) throw new ArgumentNullException("entity");
var cacheKey = GetCacheIdKey(id);
try
{
persistDeleted(entity);
}
finally
{
// whatever happens, clear the cache
var cacheKey = GetEntityCacheKey(entity.Id);
Cache.ClearCacheItem(cacheKey);
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
}
/// <inheritdoc />
public override TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
var cacheKey = GetEntityCacheKey(id);
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
// if found in cache then return else fetch and cache
if (fromCache != null)
return fromCache;
var entity = getFromRepo(id);
var entity = performGet(id);
//set the disposal action
SetCacheAction(cacheKey, entity);
if (entity != null && entity.HasIdentity)
InsertEntity(cacheKey, entity);
return entity;
}
public override TEntity Get(TId id)
/// <inheritdoc />
public override TEntity GetCached(TId id)
{
var cacheKey = GetCacheIdKey(id);
var cacheKey = GetEntityCacheKey(id);
return Cache.GetCacheItem<TEntity>(cacheKey);
}
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
/// <inheritdoc />
public override bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
var cacheKey = GetCacheIdKey(id);
// if found in cache the return else check
var cacheKey = GetEntityCacheKey(id);
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
return fromCache != null || getFromRepo(id);
return fromCache != null || performExists(id);
}
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
/// <inheritdoc />
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
if (ids.Any())
if (ids.Length > 0)
{
var entities = ids.Select(Get).ToArray();
if (ids.Length.Equals(entities.Length) && entities.Any(x => x == null) == false)
return entities;
// try to get each entity from the cache
// if we can find all of them, return
var entities = ids.Select(GetCached).WhereNotNull().ToArray();
if (ids.Length.Equals(entities.Length))
return entities; // no need for null checks, we are not caching nulls
}
else
{
var allEntities = GetAllFromCache();
if (allEntities.Any())
// get everything we have
var entities = Cache.GetCacheItemsByKeySearch<TEntity>(GetEntityTypeCacheKey())
.ToArray(); // no need for null checks, we are not caching nulls
if (entities.Length > 0)
{
// if some of them were in the cache...
if (_options.GetAllCacheValidateCount)
{
//Get count of all entities of current type (TEntity) to ensure cached result is correct
// need to validate the count, get the actual count and return if ok
var totalCount = _options.PerformCount();
if (allEntities.Length == totalCount)
return allEntities;
if (entities.Length == totalCount)
return entities;
}
else
{
return allEntities;
// no need to validate, just return what we have and assume it's all there is
return entities;
}
}
else if (_options.GetAllCacheAllowZeroCount)
{
//if the repository allows caching a zero count, then check the zero count cache
if (HasZeroCountCache())
{
//there is a zero count cache so return an empty list
return new TEntity[] {};
}
// if none of them were in the cache
// and we allow zero count - check for the special (empty) entry
var empty = Cache.GetCacheItem<TEntity[]>(GetEntityTypeCacheKey());
if (empty != null) return empty;
}
}
//we need to do the lookup from the repo
var entityCollection = getFromRepo(ids)
//ensure we don't include any null refs in the returned collection!
.WhereNotNull()
// cache failed, get from repo and cache
var repoEntities = performGetAll(ids)
.WhereNotNull() // exclude nulls!
.Where(x => x.HasIdentity) // be safe, though would be weird...
.ToArray();
//set the disposal action
SetCacheAction(ids, entityCollection);
// note: if empty & allow zero count, will cache a special (empty) entry
InsertEntities(ids, repoEntities);
return entityCollection;
return repoEntities;
}
/// <summary>
/// Looks up the zero count cache, must return null if it doesn't exist
/// </summary>
/// <returns></returns>
protected bool HasZeroCountCache()
/// <inheritdoc />
public override void ClearAll()
{
var zeroCount = Cache.GetCacheItem<TEntity[]>(GetCacheTypeKey());
return (zeroCount != null && zeroCount.Any() == false);
// fixme the cache should NOT contain anything else so we can clean all, can't we?
Cache.ClearAllCache();
//Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
}
/// <summary>
/// Performs the lookup for all entities of this type from the cache
/// </summary>
/// <returns></returns>
protected TEntity[] GetAllFromCache()
{
var allEntities = Cache.GetCacheItemsByKeySearch<TEntity>(GetCacheTypeKey())
.WhereNotNull()
.ToArray();
return allEntities.Any() ? allEntities : new TEntity[] {};
}
/// <summary>
/// Sets the action to execute on disposal for a single entity
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="entity"></param>
protected virtual void SetCacheAction(string cacheKey, TEntity entity)
{
if (entity == null) return;
SetCacheAction(() =>
{
//just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.InsertCacheItem(cacheKey, () => entity,
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
});
}
/// <summary>
/// Sets the action to execute on disposal for an entity collection
/// </summary>
/// <param name="ids"></param>
/// <param name="entityCollection"></param>
protected virtual void SetCacheAction(TId[] ids, TEntity[] entityCollection)
{
SetCacheAction(() =>
{
//This option cannot execute if we are looking up specific Ids
if (ids.Any() == false && entityCollection.Length == 0 && _options.GetAllCacheAllowZeroCount)
{
//there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache
// to signify that there is a zero count cache
//NOTE: Don't set expiry/sliding for a zero count
Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {});
}
else
{
//This is the default behavior, we'll individually cache each item so that if/when these items are resolved
// by id, they are returned from the already existing cache.
foreach (var entity in entityCollection.WhereNotNull())
{
var localCopy = entity;
//just to be safe, we cannot cache an item without an identity
if (localCopy.HasIdentity)
{
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy,
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
}
}
});
}
}
}
@@ -1,27 +0,0 @@
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Creates cache policies
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
internal class DefaultRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private readonly IRuntimeCacheProvider _runtimeCache;
private readonly RepositoryCachePolicyOptions _options;
public DefaultRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
{
_runtimeCache = runtimeCache;
_options = options;
}
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
{
return new DefaultRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
}
}
}
@@ -3,227 +3,178 @@ using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Collections;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A caching policy that caches an entire dataset as a single collection
/// Represents a caching policy that caches the entire entities set as a single collection.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TId">The type of the identifier.</typeparam>
/// <remarks>
/// <para>Caches the entire set of entities as a single collection.</para>
/// <para>Used by Content-, Media- and MemberTypeRepository, DataTypeRepository, DomainRepository,
/// LanguageRepository, PublicAccessRepository, TemplateRepository... things that make sense to
/// keep as a whole in memory.</para>
/// </remarks>
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private readonly Func<TEntity, TId> _getEntityId;
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
private readonly Func<TEntity, TId> _entityGetId;
private readonly bool _expires;
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> entityGetId, bool expires)
: base(cache)
{
_getEntityId = getEntityId;
_getAllFromRepo = getAllFromRepo;
_entityGetId = entityGetId;
_expires = expires;
}
private bool? _hasZeroCountCache;
public override IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
return new ScopedRepositoryCachePolicy<TEntity, TId>(this, runtimeCache, scope);
}
protected static readonly TId[] EmptyIds = new TId[0]; // const
protected string GetCacheTypeKey()
protected string GetEntityTypeCacheKey()
{
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
}
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
protected void InsertEntities(TEntity[] entities)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
// cache is expected to be a deep-cloning cache ie it deep-clones whatever is
// IDeepCloneable when it goes in, and out. it also resets dirty properties,
// making sure that no 'dirty' entity is cached.
//
// this policy is caching the entire list of entities. to ensure that entities
// are properly deep-clones when cached, it uses a DeepCloneableList. however,
// we don't want to deep-clone *each* entity in the list when fetching it from
// cache as that would not be efficient for Get(id). so the DeepCloneableList is
// set to ListCloneBehavior.CloneOnce ie it will clone *once* when inserting,
// and then will *not* clone when retrieving.
try
if (_expires)
{
persistMethod(entity);
//set the disposal action
SetCacheAction(() =>
{
//Clear all
Cache.ClearCacheItem(GetCacheTypeKey());
});
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
}
catch
else
{
//set the disposal action
SetCacheAction(() =>
{
//Clear all
Cache.ClearCacheItem(GetCacheTypeKey());
});
throw;
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities));
}
}
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
/// <inheritdoc />
public override void Create(TEntity entity, Action<TEntity> persistNew)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
try
{
persistMethod(entity);
persistNew(entity);
}
finally
{
//set the disposal action
SetCacheAction(() =>
{
//Clear all
Cache.ClearCacheItem(GetCacheTypeKey());
});
ClearAll();
}
}
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
/// <inheritdoc />
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
{
//Force get all with cache
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
if (entity == null) throw new ArgumentNullException("entity");
//we don't have anything in cache (this should never happen), just return from the repo
if (found == null) return getFromRepo(id);
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
if (entity == null) return null;
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
return (TEntity)entity.DeepClone();
}
public override TEntity Get(TId id)
{
//Force get all with cache
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
//we don't have anything in cache (this should never happen), just return null
if (found == null) return null;
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
if (entity == null) return null;
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
return (TEntity)entity.DeepClone();
}
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
{
//Force get all with cache
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
//we don't have anything in cache (this should never happen), just return from the repo
return found == null
? getFromRepo(id)
: found.Any(x => _getEntityId(x).Equals(id));
}
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
{
//process getting all including setting the cache callback
var result = PerformGetAll(getFromRepo);
//now that the base result has been calculated, they will all be cached.
// Now we can just filter by ids if they have been supplied
return (ids.Any()
? result.Where(x => ids.Contains(_getEntityId(x))).ToArray()
: result)
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
.Select(x => (TEntity)x.DeepClone())
.ToArray();
}
private TEntity[] PerformGetAll(Func<TId[], IEnumerable<TEntity>> getFromRepo)
{
var allEntities = GetAllFromCache();
if (allEntities.Any())
try
{
return allEntities;
persistUpdated(entity);
}
//check the zero count cache
if (HasZeroCountCache())
finally
{
//there is a zero count cache so return an empty list
return new TEntity[] { };
ClearAll();
}
//we need to do the lookup from the repo
var entityCollection = getFromRepo(new TId[] { })
//ensure we don't include any null refs in the returned collection!
.WhereNotNull()
.ToArray();
//set the disposal action
SetCacheAction(entityCollection);
return entityCollection;
}
/// <summary>
/// For this type of caching policy, we don't cache individual items
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="entity"></param>
protected void SetCacheAction(string cacheKey, TEntity entity)
/// <inheritdoc />
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
//No-op
}
if (entity == null) throw new ArgumentNullException("entity");
/// <summary>
/// Sets the action to execute on disposal for an entity collection
/// </summary>
/// <param name="entityCollection"></param>
protected void SetCacheAction(TEntity[] entityCollection)
{
//set the disposal action
SetCacheAction(() =>
try
{
//We want to cache the result as a single collection
if (_expires)
{
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection),
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
else
{
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection));
}
});
persistDeleted(entity);
}
finally
{
ClearAll();
}
}
/// <summary>
/// Looks up the zero count cache, must return null if it doesn't exist
/// </summary>
/// <returns></returns>
protected bool HasZeroCountCache()
/// <inheritdoc />
public override TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
if (_hasZeroCountCache.HasValue)
return _hasZeroCountCache.Value;
// get all from the cache, then look for the entity
var all = GetAllCached(performGetAll);
var entity = all.FirstOrDefault(x => _entityGetId(x).Equals(id));
_hasZeroCountCache = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey()) != null;
return _hasZeroCountCache.Value;
// see note in InsertEntities - what we get here is the original
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
return entity == null ? null : (TEntity) entity.DeepClone();
}
/// <summary>
/// This policy will cache the full data set as a single collection
/// </summary>
/// <returns></returns>
protected TEntity[] GetAllFromCache()
/// <inheritdoc />
public override TEntity GetCached(TId id)
{
var found = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey());
// get all from the cache -- and only the cache, then look for the entity
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
var entity = all == null ? null : all.FirstOrDefault(x => _entityGetId(x).Equals(id));
//This method will get called before checking for zero count cache, so we'll just set the flag here
_hasZeroCountCache = found != null;
return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray();
// see note in InsertEntities - what we get here is the original
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
return entity == null ? null : (TEntity)entity.DeepClone();
}
/// <inheritdoc />
public override bool Exists(TId id, Func<TId, bool> performExits, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
// get all as one set, then look for the entity
var all = GetAllCached(performGetAll);
return all.Any(x => _entityGetId(x).Equals(id));
}
/// <inheritdoc />
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
// get all as one set, from cache if possible, else repo
var all = GetAllCached(performGetAll);
// if ids have been specified, filter
if (ids.Length > 0) all = all.Where(x => ids.Contains(_entityGetId(x)));
// and return
// see note in SetCacheActionToInsertEntities - what we get here is the original
// cached entities, not clones, so we need to manually ensure they are deep-cloned.
return all.Select(x => (TEntity) x.DeepClone()).ToArray();
}
// does NOT clone anything, so be nice with the returned values
private IEnumerable<TEntity> GetAllCached(Func<TId[], IEnumerable<TEntity>> performGetAll)
{
// try the cache first
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
if (all != null) return all.ToArray();
// else get from repo and cache
var entities = performGetAll(EmptyIds).WhereNotNull().ToArray();
InsertEntities(entities); // may be an empty array...
return entities;
}
/// <inheritdoc />
public override void ClearAll()
{
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
}
}
@@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Creates cache policies
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
internal class FullDataSetRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private readonly IRuntimeCacheProvider _runtimeCache;
private readonly Func<TEntity, TId> _getEntityId;
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
private readonly bool _expires;
public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
{
_runtimeCache = runtimeCache;
_getEntityId = getEntityId;
_getAllFromRepo = getAllFromRepo;
_expires = expires;
}
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
{
return new FullDataSetRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _getEntityId, _getAllFromRepo, _expires);
}
}
}
@@ -1,18 +1,90 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
internal interface IRepositoryCachePolicy<TEntity, TId> : IDisposable
internal interface IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
TEntity Get(TId id);
bool Exists(TId id, Func<TId, bool> getFromRepo);
void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
void Remove(TEntity entity, Action<TEntity> persistMethod);
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
// note:
// at the moment each repository instance creates its corresponding cache policy instance
// we could reduce allocations by using static cache policy instances but then we would need
// to modify all methods here to pass the repository and cache eg:
//
// TEntity Get(TRepository repository, IRuntimeCacheProvider cache, TId id);
//
// it is not *that* complicated but then RepositoryBase needs to have a TRepository generic
// type parameter and it all becomes convoluted - keeping it simple for the time being.
// fixme explain
IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope);
/// <summary>
/// Gets an entity from the cache, else from the repository.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="performGet">The repository PerformGet method.</param>
/// <param name="performGetAll">The repository PerformGetAll method.</param>
/// <returns>The entity with the specified identifier, if it exits, else null.</returns>
/// <remarks>First considers the cache then the repository.</remarks>
TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll);
/// <summary>
/// Gets an entity from the cache.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>The entity with the specified identifier, if it is in the cache already, else null.</returns>
/// <remarks>Does not consider the repository at all.</remarks>
TEntity GetCached(TId id);
/// <summary>
/// Gets a value indicating whether an entity with a specified identifier exists.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="performExists">The repository PerformExists method.</param>
/// <param name="performGetAll">The repository PerformGetAll method.</param>
/// <returns>A value indicating whether an entity with the specified identifier exists.</returns>
/// <remarks>First considers the cache then the repository.</remarks>
bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll);
/// <summary>
/// Creates an entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <param name="persistNew">The repository PersistNewItem method.</param>
/// <remarks>Creates the entity in the repository, and updates the cache accordingly.</remarks>
void Create(TEntity entity, Action<TEntity> persistNew);
/// <summary>
/// Updates an entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <param name="persistUpdated">The reopsitory PersistUpdatedItem method.</param>
/// <remarks>Updates the entity in the repository, and updates the cache accordingly.</remarks>
void Update(TEntity entity, Action<TEntity> persistUpdated);
/// <summary>
/// Removes an entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <param name="persistDeleted">The repository PersistDeletedItem method.</param>
/// <remarks>Removes the entity from the repository and clears the cache.</remarks>
void Delete(TEntity entity, Action<TEntity> persistDeleted);
/// <summary>
/// Gets entities.
/// </summary>
/// <param name="ids">The identifiers.</param>
/// <param name="performGetAll">The repository PerformGetAll method.</param>
/// <returns>If <paramref name="ids"/> is empty, all entities, else the entities with the specified identifiers.</returns>
/// <remarks>Get all the entities. Either from the cache or the repository depending on the implementation.</remarks>
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll);
/// <summary>
/// Clears the entire cache.
/// </summary>
void ClearAll();
}
}
@@ -1,9 +0,0 @@
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
internal interface IRepositoryCachePolicyFactory<TEntity, TId> where TEntity : class, IAggregateRoot
{
IRepositoryCachePolicy<TEntity, TId> CreatePolicy();
}
}
@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
internal class NoRepositoryCachePolicy<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private static readonly NoRepositoryCachePolicy<TEntity, TId> StaticInstance = new NoRepositoryCachePolicy<TEntity, TId>();
private NoRepositoryCachePolicy()
{ }
public static NoRepositoryCachePolicy<TEntity, TId> Instance { get { return StaticInstance; } }
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
throw new NotImplementedException();
}
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performGet(id);
}
public TEntity GetCached(TId id)
{
return null;
}
public bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performExists(id);
}
public void Create(TEntity entity, Action<TEntity> persistNew)
{
persistNew(entity);
}
public void Update(TEntity entity, Action<TEntity> persistUpdated)
{
persistUpdated(entity);
}
public void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
persistDeleted(entity);
}
public TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performGetAll(ids).ToArray();
}
public void ClearAll()
{ }
}
}
@@ -1,27 +0,0 @@
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Creates cache policies
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
internal class OnlySingleItemsRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private readonly IRuntimeCacheProvider _runtimeCache;
private readonly RepositoryCachePolicyOptions _options;
public OnlySingleItemsRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
{
_runtimeCache = runtimeCache;
_options = options;
}
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
{
return new SingleItemsOnlyRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
}
}
}
@@ -1,48 +1,51 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
/// <summary>
/// A base class for repository cache policies.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TId">The type of the identifier.</typeparam>
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private Action _action;
protected RepositoryCachePolicyBase(IRuntimeCacheProvider cache)
{
if (cache == null) throw new ArgumentNullException("cache");
if (cache == null) throw new ArgumentNullException("cache");
Cache = cache;
}
public abstract IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope);
protected IRuntimeCacheProvider Cache { get; private set; }
/// <summary>
/// The disposal performs the caching
/// </summary>
protected override void DisposeResources()
{
if (_action != null)
{
_action();
}
}
/// <inheritdoc />
public abstract TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll);
/// <summary>
/// Sets the action to execute on disposal
/// </summary>
/// <param name="action"></param>
protected void SetCacheAction(Action action)
{
_action = action;
}
/// <inheritdoc />
public abstract TEntity GetCached(TId id);
/// <inheritdoc />
public abstract bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll);
/// <inheritdoc />
public abstract void Create(TEntity entity, Action<TEntity> persistNew);
/// <inheritdoc />
public abstract void Update(TEntity entity, Action<TEntity> persistUpdated);
/// <inheritdoc />
public abstract void Delete(TEntity entity, Action<TEntity> persistDeleted);
/// <inheritdoc />
public abstract TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll);
/// <inheritdoc />
public abstract void ClearAll();
public abstract TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
public abstract TEntity Get(TId id);
public abstract bool Exists(TId id, Func<TId, bool> getFromRepo);
public abstract void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
public abstract void Remove(TEntity entity, Action<TEntity> persistMethod);
public abstract TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
}
}
@@ -2,6 +2,9 @@ using System;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Specifies how a repository cache policy should cache entities.
/// </summary>
internal class RepositoryCachePolicyOptions
{
/// <summary>
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
internal class ScopedRepositoryCachePolicy<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private readonly IRepositoryCachePolicy<TEntity, TId> _cachePolicy;
private readonly IRuntimeCacheProvider _globalIsolatedCache;
private readonly IScope _scope;
public ScopedRepositoryCachePolicy(IRepositoryCachePolicy<TEntity, TId> cachePolicy, IRuntimeCacheProvider globalIsolatedCache, IScope scope)
{
_cachePolicy = cachePolicy;
_globalIsolatedCache = globalIsolatedCache;
_scope = scope;
}
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
throw new InvalidOperationException(); // obviously
}
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
// loads into the local cache only, ok for now
return _cachePolicy.Get(id, performGet, performGetAll);
}
public TEntity GetCached(TId id)
{
// loads into the local cache only, ok for now
return _cachePolicy.GetCached(id);
}
public bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
// loads into the local cache only, ok for now
return _cachePolicy.Exists(id, performExists, performGetAll);
}
public void Create(TEntity entity, Action<TEntity> persistNew)
{
// writes into the local cache
_cachePolicy.Create(entity, persistNew);
}
public void Update(TEntity entity, Action<TEntity> persistUpdated)
{
// writes into the local cache
_cachePolicy.Update(entity, persistUpdated);
}
public void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
// deletes the local cache
_cachePolicy.Delete(entity, persistDeleted);
}
public TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
// loads into the local cache only, ok for now
return _cachePolicy.GetAll(ids, performGetAll);
}
public void ClearAll()
{
// clears the local cache
_cachePolicy.ClearAll();
}
}
}
@@ -1,24 +1,27 @@
using System.Linq;
using Umbraco.Core.Collections;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A caching policy that ignores all caches for GetAll - it will only cache calls for individual items
/// Represents a special policy that does not cache the result of GetAll.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TId">The type of the identifier.</typeparam>
/// <remarks>
/// <para>Overrides the default repository cache policy and does not writes the result of GetAll
/// to cache, but only the result of individual Gets. It does read the cache for GetAll, though.</para>
/// <para>Used by DictionaryRepository.</para>
/// </remarks>
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) : base(cache, options)
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
: base(cache, options)
{ }
protected override void InsertEntities(TId[] ids, TEntity[] entities)
{
}
protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
{
//no-op
// nop
}
}
}
+8 -1
View File
@@ -21,6 +21,10 @@ namespace Umbraco.Core
private static readonly ICacheProvider NullRequestCache = new NullCacheProvider();
private static readonly ICacheProvider NullStaticCache = new NullCacheProvider();
private static readonly IRuntimeCacheProvider NullRuntimeCache = new NullCacheProvider();
private static readonly IsolatedRuntimeCache NullIsolatedCache = new IsolatedRuntimeCache(_ => NullRuntimeCache);
private static readonly CacheHelper NullCache = new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, NullIsolatedCache);
public static CacheHelper NoCache { get { return NullCache; } }
/// <summary>
/// Creates a cache helper with disabled caches
@@ -31,7 +35,10 @@ namespace Umbraco.Core
/// </remarks>
public static CacheHelper CreateDisabledCacheHelper()
{
return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, new IsolatedRuntimeCache(t => NullRuntimeCache));
// do *not* return NoCache
// NoCache is a special instance that is detected by RepositoryBase and disables all cache policies
// CreateDisabledCacheHelper is used in tests to use no cache, *but* keep all cache policies
return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, NullIsolatedCache);
}
/// <summary>
@@ -0,0 +1,15 @@
using System;
namespace Umbraco.Core.CodeAnnotations
{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class UmbracoUdiTypeAttribute : Attribute
{
public string UdiType { get; private set; }
public UmbracoUdiTypeAttribute(string udiType)
{
UdiType = udiType;
}
}
}
@@ -159,6 +159,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); }
}
[ConfigurationProperty("showDeprecatedPropertyEditors")]
internal InnerTextConfigurationElement<bool> ShowDeprecatedPropertyEditors
{
get { return GetOptionalTextElement("showDeprecatedPropertyEditors", false); }
}
[ConfigurationProperty("EnableInheritedDocumentTypes")]
internal InnerTextConfigurationElement<bool> EnableInheritedDocumentTypes
{
@@ -306,6 +312,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return DefaultDocumentTypeProperty; }
}
bool IContentSection.ShowDeprecatedPropertyEditors
{
get { return ShowDeprecatedPropertyEditors; }
}
bool IContentSection.EnableInheritedDocumentTypes
{
get { return EnableInheritedDocumentTypes; }
@@ -60,6 +60,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
string DefaultDocumentTypeProperty { get; }
/// <summary>
/// The default for this is false but if you would like deprecated property editors displayed
/// in the data type editor you can enable this
/// </summary>
bool ShowDeprecatedPropertyEditors { get; }
bool EnableInheritedDocumentTypes { get; }
bool EnableInheritedMediaTypes { get; }
@@ -24,7 +24,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 "alpha056"; } }
public static string CurrentComment { get { return "alpha065"; } }
// 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
+8 -8
View File
@@ -174,24 +174,24 @@ namespace Umbraco.Core
public static readonly Guid FormsFormGuid = new Guid(FormsForm);
/// <summary>
/// Guid for a Forms Workflow.
/// Guid for a Forms PreValue Source.
/// </summary>
public const string FormsWorkflow = "42D7BF9B-A362-4FEE-B45A-674D5C064B70";
public const string FormsPreValue = "42D7BF9B-A362-4FEE-B45A-674D5C064B70";
/// <summary>
/// Guid for a Forms Workflow.
/// Guid for a Forms PreValue Source.
/// </summary>
public static readonly Guid FormsWorkflowGuid = new Guid(FormsWorkflow);
public static readonly Guid FormsPreValueGuid = new Guid(FormsPreValue);
/// <summary>
/// Guid for a Forms Record.
/// Guid for a Forms DataSource.
/// </summary>
public const string FormsRecord = "CFED6CE4-9359-443E-9977-9956FEB1D867";
public const string FormsDataSource = "CFED6CE4-9359-443E-9977-9956FEB1D867";
/// <summary>
/// Guid for a Forms Record.
/// Guid for a Forms DataSource.
/// </summary>
public static readonly Guid FormsRecordGuid = new Guid(FormsRecord);
public static readonly Guid FormsDataSourceGuid = new Guid(FormsDataSource);
}
}
}
+19 -5
View File
@@ -42,10 +42,14 @@ namespace Umbraco.Core
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string ContentPicker = "158AA029-24ED-4948-939E-C3DA209E5FBA";
[Obsolete("This is an obsoleted content picker, use ContentPicker2Alias instead")]
public const string ContentPickerAlias = "Umbraco.ContentPickerAlias";
/// <summary>
/// Alias for the Content Picker datatype.
/// </summary>
public const string ContentPickerAlias = "Umbraco.ContentPickerAlias";
public const string ContentPicker2Alias = "Umbraco.ContentPicker2";
/// <summary>
/// Guid for the Date datatype.
@@ -192,11 +196,15 @@ namespace Umbraco.Core
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string MediaPicker = "EAD69342-F06D-4253-83AC-28000225583B";
[Obsolete("This is an obsoleted picker, use MediaPicker2Alias instead")]
public const string MediaPickerAlias = "Umbraco.MediaPicker";
/// <summary>
/// Alias for the Media Picker datatype.
/// </summary>
public const string MediaPickerAlias = "Umbraco.MediaPicker";
public const string MediaPicker2Alias = "Umbraco.MediaPicker2";
[Obsolete("This is an obsoleted picker, use MediaPicker2Alias instead")]
public const string MultipleMediaPickerAlias = "Umbraco.MultipleMediaPicker";
/// <summary>
@@ -205,26 +213,32 @@ namespace Umbraco.Core
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string MemberPicker = "39F533E4-0551-4505-A64B-E0425C5CE775";
[Obsolete("This is an obsoleted picker, use MemberPicker2Alias instead")]
public const string MemberPickerAlias = "Umbraco.MemberPicker";
/// <summary>
/// Alias for the Member Picker datatype.
/// </summary>
public const string MemberPickerAlias = "Umbraco.MemberPicker";
public const string MemberPicker2Alias = "Umbraco.MemberPicker2";
/// <summary>
/// Alias for the Member Group Picker datatype.
/// </summary>
public const string MemberGroupPickerAlias = "Umbraco.MemberGroupPicker";
/// <summary>
/// Guid for the Multi-Node Tree Picker datatype
/// </summary>
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string MultiNodeTreePicker = "7E062C13-7C41-4AD9-B389-41D88AEEF87C";
[Obsolete("This is an obsoleted picker, use MultiNodeTreePicker2Alias instead")]
public const string MultiNodeTreePickerAlias = "Umbraco.MultiNodeTreePicker";
/// <summary>
/// Alias for the Multi-Node Tree Picker datatype
/// </summary>
public const string MultiNodeTreePickerAlias = "Umbraco.MultiNodeTreePicker";
public const string MultiNodeTreePicker2Alias = "Umbraco.MultiNodeTreePicker2";
/// <summary>
/// Guid for the Multiple Textstring datatype.
+10 -7
View File
@@ -28,6 +28,7 @@ using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Publishing;
using Umbraco.Core.Macros;
using Umbraco.Core.Manifest;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
using Umbraco.Core.Strings;
@@ -108,8 +109,11 @@ namespace Umbraco.Core
var dbFactory = new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, ProfilingLogger.Logger);
Database.Mapper = new PetaPocoMapper();
var scopeProvider = new ScopeProvider(dbFactory);
dbFactory.ScopeProvider = scopeProvider;
var dbContext = new DatabaseContext(
dbFactory,
scopeProvider,
ProfilingLogger.Logger,
SqlSyntaxProviders.CreateDefault(ProfilingLogger.Logger));
@@ -117,7 +121,7 @@ namespace Umbraco.Core
dbContext.Initialize();
//get the service context
var serviceContext = CreateServiceContext(dbContext, dbFactory);
var serviceContext = CreateServiceContext(dbContext, scopeProvider);
//set property and singleton from response
ApplicationContext.Current = ApplicationContext = CreateApplicationContext(dbContext, serviceContext);
@@ -160,17 +164,16 @@ namespace Umbraco.Core
/// Creates and returns the service context for the app
/// </summary>
/// <param name="dbContext"></param>
/// <param name="dbFactory"></param>
/// <param name="scopeProvider"></param>
/// <returns></returns>
protected virtual ServiceContext CreateServiceContext(DatabaseContext dbContext, IDatabaseFactory dbFactory)
protected virtual ServiceContext CreateServiceContext(DatabaseContext dbContext, IScopeProvider scopeProvider)
{
//default transient factory
var msgFactory = new TransientMessagesFactory();
return new ServiceContext(
new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
new PetaPocoUnitOfWorkProvider(dbFactory),
new FileUnitOfWorkProvider(),
new PublishingStrategy(msgFactory, ProfilingLogger.Logger),
new PetaPocoUnitOfWorkProvider(scopeProvider),
new FileUnitOfWorkProvider(scopeProvider),
ApplicationCache,
ProfilingLogger.Logger,
msgFactory);
+111 -15
View File
@@ -14,6 +14,7 @@ using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Initial;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
namespace Umbraco.Core
@@ -26,7 +27,7 @@ namespace Umbraco.Core
/// </remarks>
public class DatabaseContext
{
private readonly IDatabaseFactory _factory;
internal readonly IScopeProviderInternal ScopeProvider;
private readonly ILogger _logger;
private readonly SqlSyntaxProviders _syntaxProviders;
private bool _configured;
@@ -40,6 +41,19 @@ namespace Umbraco.Core
/// </summary>
private const int ConnectionCheckMinutes = 1;
#region Compatibility with 7.5
// note: the ctors accepting IDatabaseFactory are here only for backward compatibility purpose
//
// problem: IDatabaseFactory2 adds the CreateNewDatabase() method which creates a new database
// 'cos IDatabaseFactory CreateDatabase() is supposed to also manage the ambient thing. We
// want to keep these ctors for backward compatibility reasons (in case ppl use them in tests)
// so we need to create a scope provider (else nothing would work) and so we need a IDatabaseFactory2,
// so...?
// solution: wrap IDatabaseFactory and pretend we have a IDatabaseFactory2, it *should* work in most
// cases but really, it depends on what ppl are doing in their tests... yet, cannot really see any
// other way to do it?
[Obsolete("Use the constructor specifying all dependencies instead")]
public DatabaseContext(IDatabaseFactory factory)
: this(factory, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[]
@@ -48,8 +62,7 @@ namespace Umbraco.Core
new SqlCeSyntaxProvider(),
new SqlServerSyntaxProvider()
}))
{
}
{ }
/// <summary>
/// Default constructor
@@ -63,7 +76,11 @@ namespace Umbraco.Core
if (logger == null) throw new ArgumentNullException("logger");
if (syntaxProviders == null) throw new ArgumentNullException("syntaxProviders");
_factory = factory;
var asDbFactory2 = factory as IDatabaseFactory2;
ScopeProvider = asDbFactory2 == null
? new ScopeProvider(new DatabaseFactoryWrapper(factory))
: new ScopeProvider(asDbFactory2);
_logger = logger;
_syntaxProviders = syntaxProviders;
}
@@ -80,22 +97,86 @@ namespace Umbraco.Core
_providerName = providerName;
SqlSyntax = sqlSyntax;
SqlSyntaxContext.SqlSyntaxProvider = SqlSyntax;
_factory = factory;
var asDbFactory2 = factory as IDatabaseFactory2;
ScopeProvider = asDbFactory2 == null
? new ScopeProvider(new DatabaseFactoryWrapper(factory))
: new ScopeProvider(asDbFactory2);
_logger = logger;
_configured = true;
}
#if DEBUG_DATABASES
public List<UmbracoDatabase> Databases
private class DatabaseFactoryWrapper : IDatabaseFactory2
{
get
private readonly IDatabaseFactory _factory;
public DatabaseFactoryWrapper(IDatabaseFactory factory)
{
var factory = _factory as DefaultDatabaseFactory;
if (factory == null) throw new NotSupportedException();
return factory.Databases;
_factory = factory;
}
public UmbracoDatabase CreateDatabase()
{
return _factory.CreateDatabase();
}
public UmbracoDatabase CreateNewDatabase()
{
return CreateDatabase();
}
public void Dispose()
{
_factory.Dispose();
}
}
#endif
#endregion
[Obsolete("Use the constructor specifying all dependencies instead")]
internal DatabaseContext(IScopeProviderInternal scopeProvider)
: this(scopeProvider, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[]
{
new MySqlSyntaxProvider(LoggerResolver.Current.Logger),
new SqlCeSyntaxProvider(),
new SqlServerSyntaxProvider()
}))
{ }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="scopeProvider"></param>
/// <param name="logger"></param>
/// <param name="syntaxProviders"></param>
internal DatabaseContext(IScopeProviderInternal scopeProvider, ILogger logger, SqlSyntaxProviders syntaxProviders)
{
if (scopeProvider == null) throw new ArgumentNullException("scopeProvider");
if (logger == null) throw new ArgumentNullException("logger");
if (syntaxProviders == null) throw new ArgumentNullException("syntaxProviders");
ScopeProvider = scopeProvider;
_logger = logger;
_syntaxProviders = syntaxProviders;
}
/// <summary>
/// Create a configured DatabaseContext
/// </summary>
/// <param name="scopeProvider"></param>
/// <param name="logger"></param>
/// <param name="sqlSyntax"></param>
/// <param name="providerName"></param>
internal DatabaseContext(IScopeProviderInternal scopeProvider, ILogger logger, ISqlSyntaxProvider sqlSyntax, string providerName)
{
_providerName = providerName;
SqlSyntax = sqlSyntax;
SqlSyntaxContext.SqlSyntaxProvider = SqlSyntax;
ScopeProvider = scopeProvider;
_logger = logger;
_configured = true;
}
public ISqlSyntaxProvider SqlSyntax { get; private set; }
@@ -110,7 +191,17 @@ namespace Umbraco.Core
/// </remarks>
public virtual UmbracoDatabase Database
{
get { return _factory.CreateDatabase(); }
get
{
if (IsDatabaseConfigured == false)
{
throw new InvalidOperationException("Cannot create a database instance, there is no available connection string");
}
return ScopeProvider.GetAmbientOrNoScope().Database;
//var scope = ScopeProvider.AmbientScope;
//return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database;
}
}
/// <summary>
@@ -123,6 +214,8 @@ namespace Umbraco.Core
/// will be properly removed from call context and does not interfere with anything else. In most case
/// it is not replacing anything, just temporarily installing a database in context.</para>
/// </remarks>
// fixme - this should just entirely be replaced by Scope?
/*
public virtual IDisposable UseSafeDatabase(bool force = false)
{
var factory = _factory as DefaultDatabaseFactory;
@@ -138,6 +231,7 @@ namespace Umbraco.Core
// create a new, temp, database (will be disposed with UsingDatabase)
return new UsingDatabase(null, factory.CreateDatabase());
}
*/
/// <summary>
/// Boolean indicating whether the database has been configured
@@ -159,7 +253,7 @@ namespace Umbraco.Core
//Don't check again if the timeout period hasn't elapsed
//this ensures we don't keep checking the connection too many times in a row like during startup.
//Do check if the _connectionLastChecked is null which means we're just initializing or it could
//Do check if the _connectionLastChecked is null which means we're just initializing or it could
//not connect last time it was checked.
if ((_connectionLastChecked.HasValue && (DateTime.Now - _connectionLastChecked.Value).TotalMinutes > ConnectionCheckMinutes)
|| _connectionLastChecked.HasValue == false)
@@ -202,7 +296,7 @@ namespace Umbraco.Core
}
else
{
throw new InvalidOperationException("Can't find a connection string with the name '" + Constants.System.UmbracoConnectionName + "'");
throw new NullReferenceException("Can't find a connection string with the name '" + Constants.System.UmbracoConnectionName + "'");
}
return _providerName;
}
@@ -809,6 +903,7 @@ namespace Umbraco.Core
return true;
}
/*
private class UsingDatabase : IDisposable
{
private readonly UmbracoDatabase _orig;
@@ -831,5 +926,6 @@ namespace Umbraco.Core
GC.SuppressFinalize(this);
}
}
*/
}
}
+4
View File
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Deploy
if (udi == null)
throw new ArgumentNullException("udi");
Udi = udi;
Name = Udi.ToString();
Dependencies = dependencies ?? Enumerable.Empty<ArtifactDependency>();
_checksum = new Lazy<string>(GetChecksum);
@@ -48,5 +49,8 @@ namespace Umbraco.Core.Deploy
}
#endregion
public string Name { get; set; }
public string Alias { get; set; }
}
}
+4 -1
View File
@@ -4,5 +4,8 @@ namespace Umbraco.Core.Deploy
/// Represents an artifact ie an object that can be transfered between environments.
/// </summary>
public interface IArtifact : IArtifactSignature
{ }
{
string Name { get; }
string Alias { get; }
}
}
@@ -1,8 +0,0 @@
namespace Umbraco.Core.Deploy
{
public interface IPrettyArtifact
{
string Name { get; }
string Alias { get; }
}
}
@@ -10,8 +10,8 @@ namespace Umbraco.Core.Events
/// Event args for that can support cancellation
/// </summary>
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
public class CancellableEventArgs : EventArgs
{
public class CancellableEventArgs : EventArgs, IEquatable<CancellableEventArgs>
{
private bool _cancel;
public CancellableEventArgs(bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
@@ -98,6 +98,36 @@ namespace Umbraco.Core.Events
/// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility
/// so we cannot change the strongly typed nature for some events.
/// </remarks>
public ReadOnlyDictionary<string, object> AdditionalData { get; private set; }
}
public ReadOnlyDictionary<string, object> AdditionalData { get; private set; }
public bool Equals(CancellableEventArgs other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(AdditionalData, other.AdditionalData);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((CancellableEventArgs) obj);
}
public override int GetHashCode()
{
return (AdditionalData != null ? AdditionalData.GetHashCode() : 0);
}
public static bool operator ==(CancellableEventArgs left, CancellableEventArgs right)
{
return Equals(left, right);
}
public static bool operator !=(CancellableEventArgs left, CancellableEventArgs right)
{
return !Equals(left, right);
}
}
}
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Security.Permissions;
using Umbraco.Core.Models;
@@ -10,7 +11,7 @@ namespace Umbraco.Core.Events
/// </summary>
/// <typeparam name="T"></typeparam>
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
public class CancellableObjectEventArgs<T> : CancellableEventArgs
public class CancellableObjectEventArgs<T> : CancellableEventArgs, IEquatable<CancellableObjectEventArgs<T>>
{
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
: base(canCancel, messages, additionalData)
@@ -48,5 +49,37 @@ namespace Umbraco.Core.Events
/// </remarks>
protected T EventObject { get; set; }
public bool Equals(CancellableObjectEventArgs<T> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && EqualityComparer<T>.Default.Equals(EventObject, other.EventObject);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((CancellableObjectEventArgs<T>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(EventObject);
}
}
public static bool operator ==(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
{
return Equals(left, right);
}
public static bool operator !=(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
{
return !Equals(left, right);
}
}
}
+41 -1
View File
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Events
{
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<CopyEventArgs<TEntity>>
{
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId)
: base(original, canCancel)
@@ -43,5 +46,42 @@ namespace Umbraco.Core.Events
public int ParentId { get; private set; }
public bool RelateToOriginal { get; set; }
public bool Equals(CopyEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && EqualityComparer<TEntity>.Default.Equals(Copy, other.Copy) && ParentId == other.ParentId && RelateToOriginal == other.RelateToOriginal;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((CopyEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ EqualityComparer<TEntity>.Default.GetHashCode(Copy);
hashCode = (hashCode * 397) ^ ParentId;
hashCode = (hashCode * 397) ^ RelateToOriginal.GetHashCode();
return hashCode;
}
}
public static bool operator ==(CopyEventArgs<TEntity> left, CopyEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(CopyEventArgs<TEntity> left, CopyEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
+70 -3
View File
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Events
{
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<DeleteEventArgs<TEntity>>, IDeletingMediaFilesEventArgs
{
/// <summary>
/// Constructor accepting multiple entities that are used in the delete operation
@@ -99,10 +100,43 @@ namespace Umbraco.Core.Events
/// <summary>
/// A list of media files that can be added to during a deleted operation for which Umbraco will ensure are removed
/// </summary>
public List<string> MediaFilesToDelete { get; private set; }
public List<string> MediaFilesToDelete { get; private set; }
public bool Equals(DeleteEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && MediaFilesToDelete.Equals(other.MediaFilesToDelete);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((DeleteEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ MediaFilesToDelete.GetHashCode();
}
}
public static bool operator ==(DeleteEventArgs<TEntity> left, DeleteEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(DeleteEventArgs<TEntity> left, DeleteEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
public class DeleteEventArgs : CancellableEventArgs
public class DeleteEventArgs : CancellableEventArgs, IEquatable<DeleteEventArgs>
{
public DeleteEventArgs(int id, bool canCancel, EventMessages eventMessages)
: base(canCancel, eventMessages)
@@ -125,5 +159,38 @@ namespace Umbraco.Core.Events
/// Gets the Id of the object being deleted.
/// </summary>
public int Id { get; private set; }
public bool Equals(DeleteEventArgs other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && Id == other.Id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((DeleteEventArgs) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ Id;
}
}
public static bool operator ==(DeleteEventArgs left, DeleteEventArgs right)
{
return Equals(left, right);
}
public static bool operator !=(DeleteEventArgs left, DeleteEventArgs right)
{
return !Equals(left, right);
}
}
}
@@ -2,7 +2,7 @@ using System;
namespace Umbraco.Core.Events
{
public class DeleteRevisionsEventArgs : DeleteEventArgs
public class DeleteRevisionsEventArgs : DeleteEventArgs, IEquatable<DeleteRevisionsEventArgs>
{
public DeleteRevisionsEventArgs(int id, bool canCancel, Guid specificVersion = default(Guid), bool deletePriorVersions = false, DateTime dateToRetain = default(DateTime))
: base(id, canCancel)
@@ -31,5 +31,42 @@ namespace Umbraco.Core.Events
{
get { return SpecificVersion != default(Guid); }
}
public bool Equals(DeleteRevisionsEventArgs other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && DateToRetain.Equals(other.DateToRetain) && DeletePriorVersions == other.DeletePriorVersions && SpecificVersion.Equals(other.SpecificVersion);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((DeleteRevisionsEventArgs) obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ DateToRetain.GetHashCode();
hashCode = (hashCode * 397) ^ DeletePriorVersions.GetHashCode();
hashCode = (hashCode * 397) ^ SpecificVersion.GetHashCode();
return hashCode;
}
}
public static bool operator ==(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right)
{
return Equals(left, right);
}
public static bool operator !=(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right)
{
return !Equals(left, right);
}
}
}
@@ -0,0 +1,73 @@
using System;
namespace Umbraco.Core.Events
{
internal class EventDefinition : EventDefinitionBase
{
private readonly EventHandler _trackedEvent;
private readonly object _sender;
private readonly EventArgs _args;
public EventDefinition(EventHandler trackedEvent, object sender, EventArgs args, string eventName = null)
: base(sender, args, eventName)
{
_trackedEvent = trackedEvent;
_sender = sender;
_args = args;
}
public override void RaiseEvent()
{
if (_trackedEvent != null)
{
_trackedEvent(_sender, _args);
}
}
}
internal class EventDefinition<TEventArgs> : EventDefinitionBase
{
private readonly EventHandler<TEventArgs> _trackedEvent;
private readonly object _sender;
private readonly TEventArgs _args;
public EventDefinition(EventHandler<TEventArgs> trackedEvent, object sender, TEventArgs args, string eventName = null)
: base(sender, args, eventName)
{
_trackedEvent = trackedEvent;
_sender = sender;
_args = args;
}
public override void RaiseEvent()
{
if (_trackedEvent != null)
{
_trackedEvent(_sender, _args);
}
}
}
internal class EventDefinition<TSender, TEventArgs> : EventDefinitionBase
{
private readonly TypedEventHandler<TSender, TEventArgs> _trackedEvent;
private readonly TSender _sender;
private readonly TEventArgs _args;
public EventDefinition(TypedEventHandler<TSender, TEventArgs> trackedEvent, TSender sender, TEventArgs args, string eventName = null)
: base(sender, args, eventName)
{
_trackedEvent = trackedEvent;
_sender = sender;
_args = args;
}
public override void RaiseEvent()
{
if (_trackedEvent != null)
{
_trackedEvent(_sender, _args);
}
}
}
}
@@ -0,0 +1,70 @@
using System;
using System.Reflection;
namespace Umbraco.Core.Events
{
public abstract class EventDefinitionBase : IEventDefinition, IEquatable<EventDefinitionBase>
{
protected EventDefinitionBase(object sender, object args, string eventName = null)
{
if (sender == null) throw new ArgumentNullException("sender");
if (args == null) throw new ArgumentNullException("args");
Sender = sender;
Args = args;
EventName = eventName;
if (EventName.IsNullOrWhiteSpace())
{
var findResult = EventNameExtractor.FindEvent(sender, args,
//don't match "Ing" suffixed names
exclude:EventNameExtractor.MatchIngNames);
if (findResult.Success == false)
throw new AmbiguousMatchException("Could not automatically find the event name, the event name will need to be explicitly registered for this event definition. Error: " + findResult.Result.Error);
EventName = findResult.Result.Name;
}
}
public object Sender { get; private set; }
public object Args { get; private set; }
public string EventName { get; private set; }
public abstract void RaiseEvent();
public bool Equals(EventDefinitionBase other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Args.Equals(other.Args) && string.Equals(EventName, other.EventName) && Sender.Equals(other.Sender);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((EventDefinitionBase) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Args.GetHashCode();
hashCode = (hashCode * 397) ^ EventName.GetHashCode();
hashCode = (hashCode * 397) ^ Sender.GetHashCode();
return hashCode;
}
}
public static bool operator ==(EventDefinitionBase left, EventDefinitionBase right)
{
return Equals(left, right);
}
public static bool operator !=(EventDefinitionBase left, EventDefinitionBase right)
{
return Equals(left, right) == false;
}
}
}
@@ -0,0 +1,24 @@
namespace Umbraco.Core.Events
{
/// <summary>
/// The filter used in the <see cref="IEventDispatcher"/> GetEvents method which determines
/// how the result list is filtered
/// </summary>
public enum EventDefinitionFilter
{
/// <summary>
/// Returns all events tracked
/// </summary>
All,
/// <summary>
/// Deduplicates events and only returns the first duplicate instance tracked
/// </summary>
FirstIn,
/// <summary>
/// Deduplicates events and only returns the last duplicate instance tracked
/// </summary>
LastIn
}
}
+30 -61
View File
@@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Events
{
@@ -10,73 +12,40 @@ namespace Umbraco.Core.Events
/// </summary>
public static class EventExtensions
{
/// <summary>
/// Raises the event and returns a boolean value indicating if the event was cancelled
/// </summary>
/// <typeparam name="TSender"></typeparam>
/// <typeparam name="TArgs"></typeparam>
/// <param name="eventHandler"></param>
/// <param name="args"></param>
/// <param name="sender"></param>
/// <returns></returns>
public static bool IsRaisedEventCancelled<TSender, TArgs>(
this TypedEventHandler<TSender, TArgs> eventHandler,
TArgs args,
TSender sender)
where TArgs : CancellableEventArgs
{
if (eventHandler != null)
eventHandler(sender, args);
// keep these two for backward compatibility reasons but understand that
// they are *not* part of any scope / event dispatcher / anything...
/// <summary>
/// Raises a cancelable event and returns a value indicating whether the event should be cancelled.
/// </summary>
/// <typeparam name="TSender">The type of the event source.</typeparam>
/// <typeparam name="TArgs">The type of the event data.</typeparam>
/// <param name="eventHandler">The event handler.</param>
/// <param name="args">The event source.</param>
/// <param name="sender">The event data.</param>
/// <returns>A value indicating whether the cancelable event should be cancelled</returns>
/// <remarks>A cancelable event is raised by a component when it is about to perform an action that can be canceled.</remarks>
public static bool IsRaisedEventCancelled<TSender, TArgs>(this TypedEventHandler<TSender, TArgs> eventHandler, TArgs args, TSender sender)
where TArgs : CancellableEventArgs
{
if (eventHandler == null) return args.Cancel;
eventHandler(sender, args);
return args.Cancel;
}
/// <summary>
/// Raises the event
/// Raises an event.
/// </summary>
/// <typeparam name="TSender"></typeparam>
/// <typeparam name="TArgs"></typeparam>
/// <param name="eventHandler"></param>
/// <param name="args"></param>
/// <param name="sender"></param>
public static void RaiseEvent<TSender, TArgs>(
this TypedEventHandler<TSender, TArgs> eventHandler,
TArgs args,
TSender sender)
where TArgs : EventArgs
{
if (eventHandler != null)
eventHandler(sender, args);
}
// moves the last handler that was added to an instance event, to first position
public static void PromoteLastHandler(object sender, string eventName)
{
var fieldInfo = sender.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null) throw new InvalidOperationException("No event named " + eventName + ".");
PromoteLastHandler(sender, fieldInfo);
}
// moves the last handler that was added to a static event, to first position
public static void PromoteLastHandler<TSender>(string eventName)
/// <typeparam name="TSender">The type of the event source.</typeparam>
/// <typeparam name="TArgs">The type of the event data.</typeparam>
/// <param name="eventHandler">The event handler.</param>
/// <param name="args">The event source.</param>
/// <param name="sender">The event data.</param>
public static void RaiseEvent<TSender, TArgs>(this TypedEventHandler<TSender, TArgs> eventHandler, TArgs args, TSender sender)
where TArgs : EventArgs
{
var fieldInfo = typeof(TSender).GetField(eventName, BindingFlags.Static | BindingFlags.NonPublic);
if (fieldInfo == null) throw new InvalidOperationException("No event named " + eventName + ".");
PromoteLastHandler(null, fieldInfo);
}
private static void PromoteLastHandler(object sender, FieldInfo fieldInfo)
{
var d = fieldInfo.GetValue(sender) as Delegate;
if (d == null) return;
var l = d.GetInvocationList();
var x = l[l.Length - 1];
for (var i = l.Length - 1; i > 0; i--)
l[i] = l[i - 1];
l[0] = x;
fieldInfo.SetValue(sender, Delegate.Combine(l));
if (eventHandler == null) return;
eventHandler(sender, args);
}
}
}
@@ -0,0 +1,159 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
namespace Umbraco.Core.Events
{
/// <summary>
/// There is actually no way to discover an event name in c# at the time of raising the event. It is possible
/// to get the event name from the handler that is being executed based on the event being raised, however that is not
/// what we want in this case. We need to find the event name before it is being raised - you would think that it's possible
/// with reflection or anything but that is not the case, the delegate that defines an event has no info attached to it, it
/// is literally just an event.
///
/// So what this does is take the sender and event args objects, looks up all public/static events on the sender that have
/// a generic event handler with generic arguments (but only) one, then we match the type of event arguments with the ones
/// being passed in. As it turns out, in our services this will work for the majority of our events! In some cases it may not
/// work and we'll have to supply a string but hopefully this saves a bit of magic strings.
///
/// We can also write tests to validate these are all working correctly for all services.
/// </summary>
internal class EventNameExtractor
{
/// <summary>
/// Finds the event name on the sender that matches the args type
/// </summary>
/// <param name="senderType"></param>
/// <param name="argsType"></param>
/// <param name="exclude">
/// A filter to exclude matched event names, this filter should return true to exclude the event name from being matched
/// </param>
/// <returns>
/// null if not found or an ambiguous match
/// </returns>
public static Attempt<EventNameExtractorResult> FindEvent(Type senderType, Type argsType, Func<string, bool> exclude)
{
var found = MatchedEventNames.GetOrAdd(new Tuple<Type, Type>(senderType, argsType), tuple =>
{
var events = CandidateEvents.GetOrAdd(senderType, t =>
{
return t.GetEvents(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
//we can only look for events handlers with generic types because that is the only
// way that we can try to find a matching event based on the arg type passed in
.Where(x => x.EventHandlerType.IsGenericType)
.Select(x => new EventInfoArgs(x, x.EventHandlerType.GetGenericArguments()))
//we are only looking for event handlers that have more than one generic argument
.Where(x =>
{
if (x.GenericArgs.Length == 1) return true;
//special case for our own TypedEventHandler
if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>) && x.GenericArgs.Length == 2)
{
return true;
}
return false;
})
.ToArray();
});
return events.Where(x =>
{
if (x.GenericArgs.Length == 1 && x.GenericArgs[0] == tuple.Item2)
return true;
//special case for our own TypedEventHandler
if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>)
&& x.GenericArgs.Length == 2
&& x.GenericArgs[1] == tuple.Item2)
{
return true;
}
return false;
}).Select(x => x.EventInfo.Name).ToArray();
});
var filtered = found.Where(x => exclude(x) == false).ToArray();
if (filtered.Length == 0)
return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.NoneFound));
if (filtered.Length == 1)
return Attempt.Succeed(new EventNameExtractorResult(filtered[0]));
//there's more than one left so it's ambiguous!
return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.Ambiguous));
}
/// <summary>
/// Finds the event name on the sender that matches the args type
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <param name="exclude">
/// A filter to exclude matched event names, this filter should return true to exclude the event name from being matched
/// </param>
/// <returns>
/// null if not found or an ambiguous match
/// </returns>
public static Attempt<EventNameExtractorResult> FindEvent(object sender, object args, Func<string, bool> exclude)
{
return FindEvent(sender.GetType(), args.GetType(), exclude);
}
/// <summary>
/// Return true if the event is named with an ING name such as "Saving" or "RollingBack"
/// </summary>
/// <param name="eventName"></param>
/// <returns></returns>
internal static bool MatchIngNames(string eventName)
{
var splitter = new Regex(@"(?<!^)(?=[A-Z])");
var words = splitter.Split(eventName);
if (words.Length == 0)
return false;
return words[0].EndsWith("ing");
}
/// <summary>
/// Return true if the event is not named with an ING name such as "Saving" or "RollingBack"
/// </summary>
/// <param name="eventName"></param>
/// <returns></returns>
internal static bool MatchNonIngNames(string eventName)
{
var splitter = new Regex(@"(?<!^)(?=[A-Z])");
var words = splitter.Split(eventName);
if (words.Length == 0)
return false;
return words[0].EndsWith("ing") == false;
}
private class EventInfoArgs
{
public EventInfo EventInfo { get; private set; }
public Type[] GenericArgs { get; private set; }
public EventInfoArgs(EventInfo eventInfo, Type[] genericArgs)
{
EventInfo = eventInfo;
GenericArgs = genericArgs;
}
}
/// <summary>
/// Used to cache all candidate events for a given type so we don't re-look them up
/// </summary>
private static readonly ConcurrentDictionary<Type, EventInfoArgs[]> CandidateEvents = new ConcurrentDictionary<Type, EventInfoArgs[]>();
/// <summary>
/// Used to cache all matched event names by (sender type + arg type) so we don't re-look them up
/// </summary>
private static readonly ConcurrentDictionary<Tuple<Type, Type>, string[]> MatchedEventNames = new ConcurrentDictionary<Tuple<Type, Type>, string[]>();
}
}
@@ -0,0 +1,8 @@
namespace Umbraco.Core.Events
{
internal enum EventNameExtractorError
{
NoneFound,
Ambiguous
}
}
@@ -0,0 +1,18 @@
namespace Umbraco.Core.Events
{
internal class EventNameExtractorResult
{
public EventNameExtractorError? Error { get; private set; }
public string Name { get; private set; }
public EventNameExtractorResult(string name)
{
Name = name;
}
public EventNameExtractorResult(EventNameExtractorError error)
{
Error = error;
}
}
}
+36 -2
View File
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Umbraco.Core.Events
{
public class ExportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
public class ExportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ExportEventArgs<TEntity>>
{
/// <summary>
/// Constructor accepting a single entity instance
@@ -48,5 +49,38 @@ namespace Umbraco.Core.Events
/// Returns the xml relating to the export event
/// </summary>
public XElement Xml { get; private set; }
public bool Equals(ExportEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && Equals(Xml, other.Xml);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((ExportEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0);
}
}
public static bool operator ==(ExportEventArgs<TEntity> left, ExportEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(ExportEventArgs<TEntity> left, ExportEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Umbraco.Core.Events
{
internal interface IDeletingMediaFilesEventArgs
{
List<string> MediaFilesToDelete { get; }
}
}
@@ -0,0 +1,13 @@
using System;
namespace Umbraco.Core.Events
{
public interface IEventDefinition
{
object Sender { get; }
object Args { get; }
string EventName { get; }
void RaiseEvent();
}
}
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Events
{
/// <summary>
/// Dispatches events from within a scope.
/// </summary>
/// <remarks>
/// <para>The name of the event is auto-magically discovered by matching the sender type, args type, and
/// eventHandler type. If the match is not unique, then the name parameter must be used to specify the
/// name in an explicit way.</para>
/// <para>What happens when an event is dispatched depends on the scope settings. It can be anything from
/// "trigger immediately" to "just ignore". Refer to the scope documentation for more details.</para>
/// </remarks>
public interface IEventDispatcher
{
// not sure about the Dispatch & DispatchCancelable signatures at all for now
// nor about the event name thing, etc - but let's keep it like this
/// <summary>
/// Dispatches a cancelable event.
/// </summary>
/// <param name="eventHandler">The event handler.</param>
/// <param name="sender">The object that raised the event.</param>
/// <param name="args">The event data.</param>
/// <param name="name">The optional name of the event.</param>
/// <returns>A value indicating whether the cancelable event was cancelled.</returns>
/// <remarks>See general remarks on the interface.</remarks>
bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string name = null);
/// <summary>
/// Dispatches a cancelable event.
/// </summary>
/// <param name="eventHandler">The event handler.</param>
/// <param name="sender">The object that raised the event.</param>
/// <param name="args">The event data.</param>
/// <param name="name">The optional name of the event.</param>
/// <returns>A value indicating whether the cancelable event was cancelled.</returns>
/// <remarks>See general remarks on the interface.</remarks>
bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string name = null)
where TArgs : CancellableEventArgs;
/// <summary>
/// Dispatches a cancelable event.
/// </summary>
/// <param name="eventHandler">The event handler.</param>
/// <param name="sender">The object that raised the event.</param>
/// <param name="args">The event data.</param>
/// <param name="name">The optional name of the event.</param>
/// <returns>A value indicating whether the cancelable event was cancelled.</returns>
/// <remarks>See general remarks on the interface.</remarks>
bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string name = null)
where TArgs : CancellableEventArgs;
/// <summary>
/// Dispatches an event.
/// </summary>
/// <param name="eventHandler">The event handler.</param>
/// <param name="sender">The object that raised the event.</param>
/// <param name="args">The event data.</param>
/// <param name="name">The optional name of the event.</param>
/// <remarks>See general remarks on the interface.</remarks>
void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string name = null);
/// <summary>
/// Dispatches an event.
/// </summary>
/// <param name="eventHandler">The event handler.</param>
/// <param name="sender">The object that raised the event.</param>
/// <param name="args">The event data.</param>
/// <param name="name">The optional name of the event.</param>
/// <remarks>See general remarks on the interface.</remarks>
void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string name = null);
/// <summary>
/// Dispatches an event.
/// </summary>
/// <param name="eventHandler">The event handler.</param>
/// <param name="sender">The object that raised the event.</param>
/// <param name="args">The event data.</param>
/// <param name="name">The optional name of the event.</param>
/// <remarks>See general remarks on the interface.</remarks>
void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string name = null);
/// <summary>
/// Notifies the dispatcher that the scope is exiting.
/// </summary>
/// <param name="completed">A value indicating whether the scope completed.</param>
void ScopeExit(bool completed);
/// <summary>
/// Gets the collected events.
/// </summary>
/// <returns>The collected events.</returns>
IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter);
}
}
+36 -2
View File
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Umbraco.Core.Events
{
public class ImportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
public class ImportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ImportEventArgs<TEntity>>
{
/// <summary>
/// Constructor accepting an XElement with the xml being imported
@@ -46,5 +47,38 @@ namespace Umbraco.Core.Events
/// Returns the xml relating to the import event
/// </summary>
public XElement Xml { get; private set; }
public bool Equals(ImportEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && Equals(Xml, other.Xml);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((ImportEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0);
}
}
public static bool operator ==(ImportEventArgs<TEntity> left, ImportEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(ImportEventArgs<TEntity> left, ImportEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Umbraco.Core.Packaging.Models;
namespace Umbraco.Core.Events
{
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ImportPackageEventArgs<TEntity>>
{
private readonly MetaData _packageMetaData;
@@ -21,6 +22,44 @@ namespace Umbraco.Core.Events
public MetaData PackageMetaData
{
get { return _packageMetaData; }
}
public IEnumerable<TEntity> InstallationSummary
{
get { return EventObject; }
}
public bool Equals(ImportPackageEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((ImportPackageEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ _packageMetaData.GetHashCode();
}
}
public static bool operator ==(ImportPackageEventArgs<TEntity> left, ImportPackageEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(ImportPackageEventArgs<TEntity> left, ImportPackageEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
+39 -1
View File
@@ -7,7 +7,7 @@ using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Core.Events
{
public class MigrationEventArgs : CancellableObjectEventArgs<IList<IMigration>>
public class MigrationEventArgs : CancellableObjectEventArgs<IList<IMigration>>, IEquatable<MigrationEventArgs>
{
/// <summary>
/// Constructor accepting multiple migrations that are used in the migration runner
@@ -141,5 +141,43 @@ namespace Umbraco.Core.Events
public string ProductName { get; private set; }
internal MigrationContext MigrationContext { get; private set; }
public bool Equals(MigrationEventArgs other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && ConfiguredSemVersion.Equals(other.ConfiguredSemVersion) && MigrationContext.Equals(other.MigrationContext) && string.Equals(ProductName, other.ProductName) && TargetSemVersion.Equals(other.TargetSemVersion);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MigrationEventArgs) obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ ConfiguredSemVersion.GetHashCode();
hashCode = (hashCode * 397) ^ MigrationContext.GetHashCode();
hashCode = (hashCode * 397) ^ ProductName.GetHashCode();
hashCode = (hashCode * 397) ^ TargetSemVersion.GetHashCode();
return hashCode;
}
}
public static bool operator ==(MigrationEventArgs left, MigrationEventArgs right)
{
return Equals(left, right);
}
public static bool operator !=(MigrationEventArgs left, MigrationEventArgs right)
{
return !Equals(left, right);
}
}
}
+34 -1
View File
@@ -4,7 +4,7 @@ using System.Linq;
namespace Umbraco.Core.Events
{
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<MoveEventArgs<TEntity>>
{
/// <summary>
/// Constructor accepting a collection of MoveEventInfo objects
@@ -123,5 +123,38 @@ namespace Umbraco.Core.Events
/// </summary>
[Obsolete("Retrieve the ParentId from the MoveInfoCollection property instead")]
public int ParentId { get; private set; }
public bool Equals(MoveEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && MoveInfoCollection.Equals(other.MoveInfoCollection);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MoveEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ MoveInfoCollection.GetHashCode();
}
}
public static bool operator ==(MoveEventArgs<TEntity> left, MoveEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(MoveEventArgs<TEntity> left, MoveEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
+40 -1
View File
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Events
{
public class MoveEventInfo<TEntity>
public class MoveEventInfo<TEntity> : IEquatable<MoveEventInfo<TEntity>>
{
public MoveEventInfo(TEntity entity, string originalPath, int newParentId)
{
@@ -12,5 +15,41 @@ namespace Umbraco.Core.Events
public TEntity Entity { get; set; }
public string OriginalPath { get; set; }
public int NewParentId { get; set; }
public bool Equals(MoveEventInfo<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return EqualityComparer<TEntity>.Default.Equals(Entity, other.Entity) && NewParentId == other.NewParentId && string.Equals(OriginalPath, other.OriginalPath);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MoveEventInfo<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = EqualityComparer<TEntity>.Default.GetHashCode(Entity);
hashCode = (hashCode * 397) ^ NewParentId;
hashCode = (hashCode * 397) ^ OriginalPath.GetHashCode();
return hashCode;
}
}
public static bool operator ==(MoveEventInfo<TEntity> left, MoveEventInfo<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(MoveEventInfo<TEntity> left, MoveEventInfo<TEntity> right)
{
return !Equals(left, right);
}
}
}
+40 -1
View File
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Events
{
public class NewEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
public class NewEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<NewEventArgs<TEntity>>
{
@@ -84,5 +86,42 @@ namespace Umbraco.Core.Events
/// Gets or Sets the parent IContent object.
/// </summary>
public TEntity Parent { get; private set; }
public bool Equals(NewEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && string.Equals(Alias, other.Alias) && EqualityComparer<TEntity>.Default.Equals(Parent, other.Parent) && ParentId == other.ParentId;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((NewEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ Alias.GetHashCode();
hashCode = (hashCode * 397) ^ EqualityComparer<TEntity>.Default.GetHashCode(Parent);
hashCode = (hashCode * 397) ^ ParentId;
return hashCode;
}
}
public static bool operator ==(NewEventArgs<TEntity> left, NewEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(NewEventArgs<TEntity> left, NewEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Events
{
/// <summary>
/// This event manager supports event cancellation and will raise the events as soon as they are tracked, it does not store tracked events
/// </summary>
internal class PassThroughEventDispatcher : IEventDispatcher
{
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
{
if (eventHandler == null) return args.Cancel;
eventHandler(sender, args);
return args.Cancel;
}
public bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
where TArgs : CancellableEventArgs
{
if (eventHandler == null) return args.Cancel;
eventHandler(sender, args);
return args.Cancel;
}
public bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
where TArgs : CancellableEventArgs
{
if (eventHandler == null) return args.Cancel;
eventHandler(sender, args);
return args.Cancel;
}
public void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string eventName = null)
{
if (eventHandler == null) return;
eventHandler(sender, args);
}
public void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
{
if (eventHandler == null) return;
eventHandler(sender, args);
}
public void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
{
if (eventHandler == null) return;
eventHandler(sender, args);
}
public IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter)
{
return Enumerable.Empty<IEventDefinition>();
}
public void ScopeExit(bool completed)
{ }
}
}
+36 -2
View File
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Events
{
public class PublishEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
public class PublishEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<PublishEventArgs<TEntity>>
{
/// <summary>
/// Constructor accepting multiple entities that are used in the publish operation
@@ -101,5 +102,38 @@ namespace Umbraco.Core.Events
}
public bool IsAllRepublished { get; private set; }
public bool Equals(PublishEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && IsAllRepublished == other.IsAllRepublished;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((PublishEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ IsAllRepublished.GetHashCode();
}
}
public static bool operator ==(PublishEventArgs<TEntity> left, PublishEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(PublishEventArgs<TEntity> left, PublishEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
+42 -1
View File
@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Events
{
public class RecycleBinEventArgs : CancellableEventArgs
public class RecycleBinEventArgs : CancellableEventArgs, IEquatable<RecycleBinEventArgs>, IDeletingMediaFilesEventArgs
{
public RecycleBinEventArgs(Guid nodeObjectType, Dictionary<int, IEnumerable<Property>> allPropertyData, bool emptiedSuccessfully)
: base(false)
@@ -97,6 +97,8 @@ namespace Umbraco.Core.Events
/// </remarks>
public List<string> Files { get; private set; }
public List<string> MediaFilesToDelete { get { return Files; } }
/// <summary>
/// Gets the list of all property data associated with a content id
/// </summary>
@@ -122,5 +124,44 @@ namespace Umbraco.Core.Events
{
get { return NodeObjectType == new Guid(Constants.ObjectTypes.Media); }
}
public bool Equals(RecycleBinEventArgs other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && AllPropertyData.Equals(other.AllPropertyData) && Files.Equals(other.Files) && Ids.Equals(other.Ids) && NodeObjectType.Equals(other.NodeObjectType) && RecycleBinEmptiedSuccessfully == other.RecycleBinEmptiedSuccessfully;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((RecycleBinEventArgs) obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ AllPropertyData.GetHashCode();
hashCode = (hashCode * 397) ^ Files.GetHashCode();
hashCode = (hashCode * 397) ^ Ids.GetHashCode();
hashCode = (hashCode * 397) ^ NodeObjectType.GetHashCode();
hashCode = (hashCode * 397) ^ RecycleBinEmptiedSuccessfully.GetHashCode();
return hashCode;
}
}
public static bool operator ==(RecycleBinEventArgs left, RecycleBinEventArgs right)
{
return Equals(left, right);
}
public static bool operator !=(RecycleBinEventArgs left, RecycleBinEventArgs right)
{
return !Equals(left, right);
}
}
}
@@ -17,5 +17,7 @@ namespace Umbraco.Core.Events
{
get { return EventObject; }
}
}
}
+2
View File
@@ -116,5 +116,7 @@ namespace Umbraco.Core.Events
{
get { return EventObject; }
}
}
}
@@ -0,0 +1,48 @@
using Umbraco.Core.IO;
namespace Umbraco.Core.Events
{
/// <summary>
/// This event manager is created for each scope and is aware of if it is nested in an outer scope
/// </summary>
/// <remarks>
/// The outer scope is the only scope that can raise events, the inner scope's will defer to the outer scope
/// </remarks>
internal class ScopeEventDispatcher : ScopeEventDispatcherBase
{
public ScopeEventDispatcher()
: base(true)
{ }
protected override void ScopeExitCompleted()
{
// fixme - we'd need to de-duplicate events somehow, etc - and the deduplication should be last in wins
foreach (var e in GetEvents(EventDefinitionFilter.All))
{
e.RaiseEvent();
// fixme - not sure I like doing it here - but then where? how?
var delete = e.Args as IDeletingMediaFilesEventArgs;
if (delete != null && delete.MediaFilesToDelete.Count > 0)
MediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete);
}
}
private MediaFileSystem _mediaFileSystem;
private MediaFileSystem MediaFileSystem
{
get
{
if (_mediaFileSystem != null) return _mediaFileSystem;
// fixme - insane! reading config goes cross AppDomain and serializes context?
using (new SafeCallContext())
{
return _mediaFileSystem = FileSystemProviderManager.Current.MediaFileSystem;
}
}
}
}
}
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Events
{
public abstract class ScopeEventDispatcherBase : IEventDispatcher
{
private List<IEventDefinition> _events;
private readonly bool _raiseCancelable;
protected ScopeEventDispatcherBase(bool raiseCancelable)
{
_raiseCancelable = raiseCancelable;
}
private List<IEventDefinition> Events { get { return _events ?? (_events = new List<IEventDefinition>()); } }
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
{
if (eventHandler == null) return args.Cancel;
if (_raiseCancelable == false) return args.Cancel;
eventHandler(sender, args);
return args.Cancel;
}
public bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
where TArgs : CancellableEventArgs
{
if (eventHandler == null) return args.Cancel;
if (_raiseCancelable == false) return args.Cancel;
eventHandler(sender, args);
return args.Cancel;
}
public bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
where TArgs : CancellableEventArgs
{
if (eventHandler == null) return args.Cancel;
if (_raiseCancelable == false) return args.Cancel;
eventHandler(sender, args);
return args.Cancel;
}
public void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string eventName = null)
{
if (eventHandler == null) return;
Events.Add(new EventDefinition(eventHandler, sender, args, eventName));
}
public void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
{
if (eventHandler == null) return;
Events.Add(new EventDefinition<TArgs>(eventHandler, sender, args, eventName));
}
public void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
{
if (eventHandler == null) return;
Events.Add(new EventDefinition<TSender, TArgs>(eventHandler, sender, args, eventName));
}
public IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter)
{
if (_events == null)
return Enumerable.Empty<IEventDefinition>();
switch (filter)
{
case EventDefinitionFilter.All:
return _events;
case EventDefinitionFilter.FirstIn:
var l1 = new OrderedHashSet<IEventDefinition>();
foreach (var e in _events)
{
l1.Add(e);
}
return l1;
case EventDefinitionFilter.LastIn:
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
foreach (var e in _events)
{
l2.Add(e);
}
return l2;
default:
throw new ArgumentOutOfRangeException("filter", filter, null);
}
}
public void ScopeExit(bool completed)
{
if (_events == null) return;
if (completed)
ScopeExitCompleted();
_events.Clear();
}
protected abstract void ScopeExitCompleted();
}
}
@@ -0,0 +1,58 @@
using System;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Events
{
/// <summary>
/// Stores the instance of EventMessages in the current scope.
/// </summary>
internal class ScopeLifespanMessagesFactory : IEventMessagesFactory
{
public const string ContextKey = "Umbraco.Core.Events.ScopeLifespanMessagesFactory";
private readonly IHttpContextAccessor _contextAccessor;
private readonly IScopeProviderInternal _scopeProvider;
public static ScopeLifespanMessagesFactory Current { get; private set; }
public ScopeLifespanMessagesFactory(IHttpContextAccessor contextAccesor, IScopeProvider scopeProvider)
{
if (contextAccesor == null) throw new ArgumentNullException("contextAccesor");
if (scopeProvider == null) throw new ArgumentNullException("scopeProvider");
if (scopeProvider is IScopeProviderInternal == false) throw new ArgumentException("Not IScopeProviderInternal.", "scopeProvider");
_contextAccessor = contextAccesor;
_scopeProvider = (IScopeProviderInternal) scopeProvider;
Current = this;
}
public EventMessages Get()
{
var messages = GetFromHttpContext();
if (messages != null) return messages;
var scope = _scopeProvider.GetAmbientOrNoScope();
return scope.Messages;
}
public EventMessages GetFromHttpContext()
{
if (_contextAccessor == null || _contextAccessor.Value == null) return null;
return (EventMessages)_contextAccessor.Value.Items[ContextKey];
}
public EventMessages TryGet()
{
var messages = GetFromHttpContext();
if (messages != null) return messages;
var scope = _scopeProvider.AmbientScope;
return scope == null ? null : scope.MessagesOrNull;
}
public void Set(EventMessages messages)
{
if (_contextAccessor.Value == null) return;
_contextAccessor.Value.Items[ContextKey] = messages;
}
}
}
+1 -1
View File
@@ -2,6 +2,6 @@ using System;
namespace Umbraco.Core.Events
{
[Serializable]
[Serializable]
public delegate void TypedEventHandler<in TSender, in TEventArgs>(TSender sender, TEventArgs e);
}
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using Umbraco.Core.Packaging.Models;
namespace Umbraco.Core.Events
{
internal class UninstallPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
{
private readonly MetaData _packageMetaData;
public UninstallPackageEventArgs(TEntity eventObject, bool canCancel)
: base(new[] { eventObject }, canCancel)
{
}
public UninstallPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
: base(new[] { eventObject })
{
_packageMetaData = packageMetaData;
}
public MetaData PackageMetaData
{
get { return _packageMetaData; }
}
public IEnumerable<TEntity> UninstallationSummary
{
get { return EventObject; }
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.0
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
+9
View File
@@ -0,0 +1,9 @@
using System.Web;
namespace Umbraco.Core
{
public interface IHttpContextAccessor
{
HttpContextBase Value { get; }
}
}
@@ -5,9 +5,10 @@ using System.Configuration;
using System.Linq;
using System.Reflection;
using Umbraco.Core.Configuration;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.IO
{
{
public class FileSystemProviderManager
{
private readonly FileSystemProvidersSection _config;
@@ -40,6 +41,12 @@ namespace Umbraco.Core.IO
get { return Instance; }
}
private IScopeProviderInternal ScopeProvider
{
// fixme - 'course this is bad, but enough for now
get { return ApplicationContext.Current == null ? null : ApplicationContext.Current.ScopeProvider as IScopeProviderInternal; }
}
internal FileSystemProviderManager()
{
_config = (FileSystemProvidersSection) ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders");
@@ -52,13 +59,13 @@ namespace Umbraco.Core.IO
_masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages);
_mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews);
_macroPartialFileSystem = _macroPartialFileSystemWrapper = new ShadowWrapper(_macroPartialFileSystem, "Views/MacroPartials");
_partialViewsFileSystem = _partialViewsFileSystemWrapper = new ShadowWrapper(_partialViewsFileSystem, "Views/Partials");
_stylesheetsFileSystem = _stylesheetsFileSystemWrapper = new ShadowWrapper(_stylesheetsFileSystem, "css");
_scriptsFileSystem = _scriptsFileSystemWrapper = new ShadowWrapper(_scriptsFileSystem, "scripts");
_xsltFileSystem = _xsltFileSystemWrapper = new ShadowWrapper(_xsltFileSystem, "xslt");
_masterPagesFileSystem = _masterPagesFileSystemWrapper = new ShadowWrapper(_masterPagesFileSystem, "masterpages");
_mvcViewsFileSystem = _mvcViewsFileSystemWrapper = new ShadowWrapper(_mvcViewsFileSystem, "Views");
_macroPartialFileSystem = _macroPartialFileSystemWrapper = new ShadowWrapper(_macroPartialFileSystem, "Views/MacroPartials", ScopeProvider);
_partialViewsFileSystem = _partialViewsFileSystemWrapper = new ShadowWrapper(_partialViewsFileSystem, "Views/Partials", ScopeProvider);
_stylesheetsFileSystem = _stylesheetsFileSystemWrapper = new ShadowWrapper(_stylesheetsFileSystem, "css", ScopeProvider);
_scriptsFileSystem = _scriptsFileSystemWrapper = new ShadowWrapper(_scriptsFileSystem, "scripts", ScopeProvider);
_xsltFileSystem = _xsltFileSystemWrapper = new ShadowWrapper(_xsltFileSystem, "xslt", ScopeProvider);
_masterPagesFileSystem = _masterPagesFileSystemWrapper = new ShadowWrapper(_masterPagesFileSystem, "masterpages", ScopeProvider);
_mvcViewsFileSystem = _mvcViewsFileSystemWrapper = new ShadowWrapper(_mvcViewsFileSystem, "Views", ScopeProvider);
// filesystems obtained from GetFileSystemProvider are already wrapped and do not need to be wrapped again
MediaFileSystem = GetFileSystemProvider<MediaFileSystem>();
@@ -92,7 +99,7 @@ namespace Umbraco.Core.IO
}
private readonly ConcurrentDictionary<string, ProviderConstructionInfo> _providerLookup = new ConcurrentDictionary<string, ProviderConstructionInfo>();
private readonly ConcurrentDictionary<Type, string> _aliases = new ConcurrentDictionary<Type, string>();
private readonly ConcurrentDictionary<Type, string> _aliases = new ConcurrentDictionary<Type, string>();
/// <summary>
/// Gets an underlying (non-typed) filesystem supporting a strongly-typed filesystem.
@@ -121,7 +128,7 @@ namespace Umbraco.Core.IO
// find a ctor matching the config parameters
var paramCount = providerConfig.Parameters != null ? providerConfig.Parameters.Count : 0;
var constructor = providerType.GetConstructors().SingleOrDefault(x
var constructor = providerType.GetConstructors().SingleOrDefault(x
=> x.GetParameters().Length == paramCount && x.GetParameters().All(y => providerConfig.Parameters.AllKeys.Contains(y.Name)));
if (constructor == null)
throw new InvalidOperationException(string.Format("Type {0} has no ctor matching the {1} configuration parameter(s).", providerType.FullName, paramCount));
@@ -129,7 +136,7 @@ namespace Umbraco.Core.IO
var parameters = new object[paramCount];
if (providerConfig.Parameters != null) // keeps ReSharper happy
for (var i = 0; i < paramCount; i++)
parameters[i] = providerConfig.Parameters[providerConfig.Parameters.AllKeys[i]].Value;
parameters[i] = providerConfig.Parameters[providerConfig.Parameters.AllKeys[i]].Value;
return new ProviderConstructionInfo
{
@@ -159,7 +166,7 @@ namespace Umbraco.Core.IO
var alias = _aliases.GetOrAdd(typeof (TFileSystem), fsType =>
{
// validate the ctor
var constructor = fsType.GetConstructors().SingleOrDefault(x
var constructor = fsType.GetConstructors().SingleOrDefault(x
=> x.GetParameters().Length == 1 && TypeHelper.IsTypeAssignableFrom<IFileSystem>(x.GetParameters().Single().ParameterType));
if (constructor == null)
throw new InvalidOperationException("Type " + fsType.FullName + " must inherit from FileSystemWrapper and have a constructor that accepts one parameter of type " + typeof(IFileSystem).FullName + ".");
@@ -176,8 +183,8 @@ namespace Umbraco.Core.IO
// so we are double-wrapping here
// could be optimized by having FileSystemWrapper inherit from ShadowWrapper, maybe
var innerFs = GetUnderlyingFileSystemProvider(alias);
var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias);
var fs = (TFileSystem) Activator.CreateInstance(typeof (TFileSystem), innerFs);
var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias, ScopeProvider);
var fs = (TFileSystem) Activator.CreateInstance(typeof (TFileSystem), shadowWrapper);
_wrappers.Add(shadowWrapper); // keeping a weak reference to the wrapper
return fs;
}
@@ -186,25 +193,7 @@ namespace Umbraco.Core.IO
#region Shadow
// note
// shadowing is thread-safe, but entering and exiting shadow mode is not, and there is only one
// global shadow for the entire application, so great care should be taken to ensure that the
// application is *not* doing anything else when using a shadow.
// shadow applies to well-known filesystems *only* - at the moment, any other filesystem that would
// be created directly (via ctor) or via GetFileSystemProvider<T> is *not* shadowed.
// shadow must be enabled in an app event handler before anything else ie before any filesystem
// is actually created and used - after, it is too late - enabling shadow has a neglictible perfs
// impact.
// NO! by the time an app event handler is instanciated it is already too late, see note in ctor.
//internal void EnableShadow()
//{
// if (_mvcViewsFileSystem != null) // test one of the fs...
// throw new InvalidOperationException("Cannot enable shadow once filesystems have been created.");
// _shadowEnabled = true;
//}
public ICompletable Shadow(Guid id)
internal ICompletable Shadow(Guid id)
{
var typed = _wrappers.ToArray();
var wrappers = new ShadowWrapper[typed.Length + 7];
@@ -218,7 +207,7 @@ namespace Umbraco.Core.IO
wrappers[i++] = _masterPagesFileSystemWrapper;
wrappers[i] = _mvcViewsFileSystemWrapper;
return ShadowFileSystemsScope.CreateScope(id, wrappers);
return new ShadowFileSystems(id, wrappers);
}
#endregion
+19
View File
@@ -109,5 +109,24 @@ namespace Umbraco.Core.IO
var wrapped2 = Wrapped as IFileSystem2;
return wrapped2 == null ? Wrapped.GetSize(path) : wrapped2.GetSize(path);
}
// explicitely implementing - not breaking
bool IFileSystem2.CanAddPhysical
{
get
{
var wrapped2 = Wrapped as IFileSystem2;
return wrapped2 != null && wrapped2.CanAddPhysical;
}
}
// explicitely implementing - not breaking
void IFileSystem2.AddFile(string path, string physicalPath, bool overrideIfExists, bool copy)
{
var wrapped2 = Wrapped as IFileSystem2;
if (wrapped2 == null)
throw new NotSupportedException();
wrapped2.AddFile(path, physicalPath, overrideIfExists, copy);
}
}
}
+4
View File
@@ -44,6 +44,10 @@ namespace Umbraco.Core.IO
{
long GetSize(string path);
bool CanAddPhysical { get; }
void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false);
// TODO: implement these
//
//void CreateDirectory(string path);
+50 -3
View File
@@ -4,11 +4,11 @@ using System.Globalization;
using System.Reflection;
using System.IO;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Text.RegularExpressions;
using System.Web.Hosting;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
namespace Umbraco.Core.IO
{
@@ -351,7 +351,54 @@ namespace Umbraco.Core.IO
writer.Write(contents);
}
}
}
}
/// <summary>
/// Checks if a given path is a full path including drive letter
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
// From: http://stackoverflow.com/a/35046453/5018
internal static bool IsFullPath(this string path)
{
return string.IsNullOrWhiteSpace(path) == false
&& path.IndexOfAny(Path.GetInvalidPathChars().ToArray()) == -1
&& Path.IsPathRooted(path)
&& Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false;
}
/// <summary>
/// Get properly formatted relative path from an existing absolute or relative path
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
internal static string GetRelativePath(this string path)
{
if (path.IsFullPath())
{
var rootDirectory = GetRootDirectorySafe();
var relativePath = path.ToLowerInvariant().Replace(rootDirectory.ToLowerInvariant(), string.Empty);
path = relativePath;
}
return path.EnsurePathIsApplicationRootPrefixed();
}
/// <summary>
/// Ensures that a path has `~/` as prefix
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
internal static string EnsurePathIsApplicationRootPrefixed(this string path)
{
if (path.StartsWith("~/"))
return path;
if (path.StartsWith("/") == false && path.StartsWith("\\") == false)
path = string.Format("/{0}", path);
if (path.StartsWith("~") == false)
path = string.Format("~{0}", path);
return path;
}
}
}
+62 -1
View File
@@ -7,6 +7,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -24,6 +25,7 @@ namespace Umbraco.Core.IO
{
private readonly IContentSection _contentConfig;
private readonly UploadAutoFillProperties _uploadAutoFillProperties;
private readonly ILogger _logger;
private readonly object _folderCounterLock = new object();
private long _folderCounter;
@@ -42,6 +44,7 @@ namespace Umbraco.Core.IO
public MediaFileSystem(IFileSystem wrapped, IContentSection contentConfig, ILogger logger)
: base(wrapped)
{
_logger = logger;
_contentConfig = contentConfig;
_uploadAutoFillProperties = new UploadAutoFillProperties(this, logger, contentConfig);
}
@@ -99,7 +102,7 @@ namespace Umbraco.Core.IO
}
else
{
// new scheme: path is "<cuid>-<puid>/<filename>" OR "<cuid>-<puid>-<filename>"
// new scheme: path is "<xuid>/<filename>" where xuid is a combination of cuid and puid
// default media filesystem maps to "~/media/<filepath>"
// assumes that cuid and puid keys can be trusted - and that a single property type
// for a single content cannot store two different files with the same name
@@ -435,6 +438,64 @@ namespace Umbraco.Core.IO
}
}
public void DeleteMediaFiles(IEnumerable<string> files)
{
files = files.Distinct();
Parallel.ForEach(files, file =>
{
try
{
if (file.IsNullOrWhiteSpace()) return;
if (FileExists(file) == false) return;
DeleteFile(file, true);
if (UseTheNewMediaPathScheme == false)
{
// old scheme: filepath is "<int>/<filename>" OR "<int>-<filename>"
// remove the directory if any
var dir = Path.GetDirectoryName(file);
if (string.IsNullOrWhiteSpace(dir) == false)
DeleteDirectory(dir, true);
}
else
{
// new scheme: path is "<xuid>/<filename>" where xuid is a combination of cuid and puid
// remove the directory
var dir = Path.GetDirectoryName(file);
DeleteDirectory(dir, true);
}
// I don't even understand...
/*
var relativeFilePath = GetRelativePath(file); // fixme - should be relative already
if (FileExists(relativeFilePath) == false) return;
var parentDirectory = Path.GetDirectoryName(relativeFilePath);
// don't want to delete the media folder if not using directories.
if (_contentSection.UploadAllowDirectories && parentDirectory != GetRelativePath("/"))
{
//issue U4-771: if there is a parent directory the recursive parameter should be true
DeleteDirectory(parentDirectory, string.IsNullOrEmpty(parentDirectory) == false);
}
else
{
DeleteFile(file, true);
}
*/
}
catch (Exception e)
{
_logger.Error<MediaFileSystem>("Failed to delete attached file \"" + file + "\".", e);
}
});
}
#endregion
#region GenerateThumbnails
+23
View File
@@ -364,6 +364,29 @@ namespace Umbraco.Core.IO
return file.Exists ? file.Length : -1;
}
public bool CanAddPhysical { get { return true; } }
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
{
var fullPath = GetFullPath(path);
if (File.Exists(fullPath))
{
if (overrideIfExists == false)
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
File.Delete(fullPath);
}
var directory = Path.GetDirectoryName(fullPath);
if (directory == null) throw new InvalidOperationException("Could not get directory.");
Directory.CreateDirectory(directory); // ensure it exists
if (copy)
File.Copy(physicalPath, fullPath);
else
File.Move(physicalPath, fullPath);
}
#region Helper Methods
protected virtual void EnsureDirectory(string path)
+82 -11
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Umbraco.Core.IO
{
@@ -33,8 +34,16 @@ namespace Umbraco.Core.IO
{
try
{
using (var stream = _sfs.OpenFile(kvp.Key))
_fs.AddFile(kvp.Key, stream, true);
var fs2 = _fs as IFileSystem2;
if (fs2 != null && fs2.CanAddPhysical)
{
fs2.AddFile(kvp.Key, _sfs.GetFullPath(kvp.Key)); // overwrite, move
}
else
{
using (var stream = _sfs.OpenFile(kvp.Key))
_fs.AddFile(kvp.Key, stream, true);
}
}
catch (Exception e)
{
@@ -201,19 +210,20 @@ namespace Umbraco.Core.IO
public IEnumerable<string> GetFiles(string path)
{
var normPath = NormPath(path);
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
var files = _fs.GetFiles(path);
return files
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
.Select(kvp => kvp.Key))
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist).Select(kvp => kvp.Key))
.Distinct();
return GetFiles(path, null);
}
public IEnumerable<string> GetFiles(string path, string filter)
{
return _fs.GetFiles(path, filter);
var normPath = NormPath(path);
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
var files = filter != null ? _fs.GetFiles(path, filter) : _fs.GetFiles(path);
var regexFilter = FilterToRegex(filter);
return files
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
.Select(kvp => kvp.Key))
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist && FilterByRegex(kvp.Key, regexFilter)).Select(kvp => kvp.Key))
.Distinct();
}
public Stream OpenFile(string path)
@@ -245,6 +255,9 @@ namespace Umbraco.Core.IO
public string GetFullPath(string path)
{
ShadowNode sf;
if (Nodes.TryGetValue(NormPath(path), out sf))
return sf.IsDir || sf.IsDelete ? null : _sfs.GetFullPath(path);
return _fs.GetFullPath(path);
}
@@ -282,5 +295,63 @@ namespace Umbraco.Core.IO
if (sf.IsDelete || sf.IsDir) throw new InvalidOperationException("Invalid path.");
return _sfs.GetSize(path);
}
public bool CanAddPhysical { get { return true; } }
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
{
ShadowNode sf;
var normPath = NormPath(path);
if (Nodes.TryGetValue(normPath, out sf) && sf.IsExist && (sf.IsDir || overrideIfExists == false))
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
var parts = normPath.Split('/');
for (var i = 0; i < parts.Length - 1; i++)
{
var dirPath = string.Join("/", parts.Take(i + 1));
ShadowNode sd;
if (Nodes.TryGetValue(dirPath, out sd))
{
if (sd.IsFile) throw new InvalidOperationException("Invalid path.");
if (sd.IsDelete) Nodes[dirPath] = new ShadowNode(false, true);
}
else
{
if (_fs.DirectoryExists(dirPath)) continue;
if (_fs.FileExists(dirPath)) throw new InvalidOperationException("Invalid path.");
Nodes[dirPath] = new ShadowNode(false, true);
}
}
_sfs.AddFile(path, physicalPath, overrideIfExists, copy);
Nodes[normPath] = new ShadowNode(false, false);
}
/// <summary>
/// Helper function for filtering keys by Regex if a filter is specified.
/// </summary>
/// <param name="input"></param>
/// <param name="regexFilter"></param>
/// <returns></returns>
internal static bool FilterByRegex(string input, string regexFilter)
{
if (regexFilter == null) return true;
return regexFilter != string.Empty && Regex.IsMatch(input, regexFilter);
}
/// <summary>
/// Transforms a filter pattern into a Regex pattern
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
/// <remarks>
/// Appending '$' only if not containing wildcard is stupid and broken.
/// It is however what seems to be what they're doing in .NET so we need to match the functionality.
/// </remarks>
internal static string FilterToRegex(string pattern)
{
if (pattern == null) return null;
return "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + (pattern.Contains("*") ? string.Empty : "$");
}
}
}
+73
View File
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Logging;
namespace Umbraco.Core.IO
{
internal class ShadowFileSystems : ICompletable
{
// note: taking a reference to the _manager instead of using manager.Current
// to avoid using Current everywhere but really, we support only 1 scope at
// a time, not multiple scopes in case of multiple managers (not supported)
private static readonly object Locker = new object();
private static Guid _currentId = Guid.Empty;
private readonly Guid _id;
private readonly ShadowWrapper[] _wrappers;
private bool _completed;
public ShadowFileSystems(Guid id, ShadowWrapper[] wrappers)
{
lock (Locker)
{
if (_currentId != Guid.Empty)
throw new InvalidOperationException("Already shadowing.");
_currentId = id;
LogHelper.Debug<ShadowFileSystems>("Shadow " + id + ".");
_id = id;
_wrappers = wrappers;
foreach (var wrapper in _wrappers)
wrapper.Shadow(id);
}
}
public void Complete()
{
_completed = true;
}
public void Dispose()
{
lock (Locker)
{
LogHelper.Debug<ShadowFileSystems>("UnShadow " + _id + " (" + (_completed ? "complete" : "abort") + ").");
var exceptions = new List<Exception>();
foreach (var wrapper in _wrappers)
{
try
{
// this may throw an AggregateException if some of the changes could not be applied
wrapper.UnShadow(_completed);
}
catch (AggregateException ae)
{
exceptions.Add(ae);
}
}
_currentId = Guid.Empty;
if (exceptions.Count > 0)
throw new AggregateException(_completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions);
}
}
// for tests
internal static void ResetId()
{
_currentId = Guid.Empty;
}
}
}
@@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using Umbraco.Core.Logging;
namespace Umbraco.Core.IO
{
internal class ShadowFileSystemsScope : ICompletable
{
// note: taking a reference to the _manager instead of using manager.Current
// to avoid using Current everywhere but really, we support only 1 scope at
// a time, not multiple scopes in case of multiple managers (not supported)
private const string ItemKey = "Umbraco.Core.IO.ShadowFileSystemsScope";
private static readonly object Locker = new object();
private readonly Guid _id;
private readonly ShadowWrapper[] _wrappers;
static ShadowFileSystemsScope()
{
SafeCallContext.Register(
() =>
{
var scope = CallContext.LogicalGetData(ItemKey);
CallContext.FreeNamedDataSlot(ItemKey);
return scope;
},
o =>
{
if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException();
if (o != null) CallContext.LogicalSetData(ItemKey, o);
});
}
private ShadowFileSystemsScope(Guid id, ShadowWrapper[] wrappers)
{
LogHelper.Debug<ShadowFileSystemsScope>("Shadow " + id + ".");
_id = id;
_wrappers = wrappers;
foreach (var wrapper in _wrappers)
wrapper.Shadow(id);
}
// internal for tests + FileSystemProviderManager
// do NOT use otherwise
internal static ShadowFileSystemsScope CreateScope(Guid id, ShadowWrapper[] wrappers)
{
lock (Locker)
{
if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException("Already shadowing.");
CallContext.LogicalSetData(ItemKey, ItemKey); // value does not matter
}
return new ShadowFileSystemsScope(id, wrappers);
}
internal static bool InScope
{
get { return NoScope == false; }
}
internal static bool NoScope
{
get { return CallContext.LogicalGetData(ItemKey) == null; }
}
public void Complete()
{
lock (Locker)
{
LogHelper.Debug<ShadowFileSystemsScope>("UnShadow " + _id + " (complete).");
var exceptions = new List<Exception>();
foreach (var wrapper in _wrappers)
{
try
{
// this may throw an AggregateException if some of the changes could not be applied
wrapper.UnShadow(true);
}
catch (AggregateException ae)
{
exceptions.Add(ae);
}
}
if (exceptions.Count > 0)
throw new AggregateException("Failed to apply all changes (see exceptions).", exceptions);
// last, & *only* if successful (otherwise we'll unshadow & cleanup as best as we can)
CallContext.FreeNamedDataSlot(ItemKey);
}
}
public void Dispose()
{
lock (Locker)
{
if (CallContext.LogicalGetData(ItemKey) == null) return;
try
{
LogHelper.Debug<ShadowFileSystemsScope>("UnShadow " + _id + " (abort)");
foreach (var wrapper in _wrappers)
wrapper.UnShadow(false); // should not throw
}
finally
{
// last, & always
CallContext.FreeNamedDataSlot(ItemKey);
}
}
}
}
}
+29 -2
View File
@@ -2,20 +2,23 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.IO
{
internal class ShadowWrapper : IFileSystem2
{
private readonly IScopeProviderInternal _scopeProvider;
private readonly IFileSystem _innerFileSystem;
private readonly string _shadowPath;
private ShadowFileSystem _shadowFileSystem;
private string _shadowDir;
public ShadowWrapper(IFileSystem innerFileSystem, string shadowPath)
public ShadowWrapper(IFileSystem innerFileSystem, string shadowPath, IScopeProviderInternal scopeProvider)
{
_innerFileSystem = innerFileSystem;
_shadowPath = shadowPath;
_scopeProvider = scopeProvider;
}
internal void Shadow(Guid id)
@@ -62,7 +65,14 @@ namespace Umbraco.Core.IO
private IFileSystem FileSystem
{
get { return ShadowFileSystemsScope.NoScope ? _innerFileSystem : _shadowFileSystem; }
get
{
var isScoped = _scopeProvider != null && _scopeProvider.AmbientScope != null && _scopeProvider.AmbientScope.ScopedFileSystems;
return isScoped
? _shadowFileSystem
: _innerFileSystem;
}
}
public IEnumerable<string> GetDirectories(string path)
@@ -154,5 +164,22 @@ namespace Umbraco.Core.IO
var filesystem2 = filesystem as IFileSystem2;
return filesystem2 == null ? filesystem.GetSize(path) : filesystem2.GetSize(path);
}
public bool CanAddPhysical
{
get
{
var fileSystem2 = FileSystem as IFileSystem2;
return fileSystem2 != null && fileSystem2.CanAddPhysical;
}
}
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
{
var fileSystem2 = FileSystem as IFileSystem2;
if (fileSystem2 == null)
throw new NotSupportedException();
fileSystem2.AddFile(path, physicalPath, overrideIfExists, copy);
}
}
}
+1 -1
View File
@@ -400,7 +400,7 @@ namespace Umbraco.Core.Models
/// <summary>
/// Set property values by alias with an annonymous object
/// </summary>
public static void PropertyValues(this IContent content, object value)
public static void PropertyValues(this IContentBase content, object value)
{
if (value == null)
throw new Exception("No properties has been passed in");
@@ -1,32 +1,72 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
//fixme: This whole thing needs to go, it's super hacky and doens't need to exist in the first place
internal static class ContentTypeExtensions
{
/// <summary>
/// Get all descendant content types
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType)
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType, IContentTypeService contentTypeService)
{
var contentTypeService = ApplicationContext.Current.Services.ContentTypeService;
var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
.SelectRecursive(type => contentTypeService.GetContentTypeChildren(type.Id));
return descendants;
if (contentType is IContentType)
{
var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
.SelectRecursive(type => contentTypeService.GetContentTypeChildren(type.Id));
return descendants;
}
if (contentType is IMediaType)
{
var descendants = contentTypeService.GetMediaTypeChildren(contentType.Id)
.SelectRecursive(type => contentTypeService.GetMediaTypeChildren(type.Id));
return descendants;
}
throw new NotSupportedException("The content type must be " + typeof(IContentType) + " or " + typeof(IMediaType));
}
/// <summary>
/// Get all descendant content types
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType, ContentTypeServiceBase contentTypeService)
{
var cService = contentTypeService as IContentTypeService;
return cService == null ? Enumerable.Empty<IContentTypeBase>() : contentType.Descendants(cService);
}
/// <summary>
/// Get all descendant and self content types
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<IContentTypeBase> DescendantsAndSelf(this IContentTypeBase contentType)
public static IEnumerable<IContentTypeBase> DescendantsAndSelf(this IContentTypeBase contentType, IContentTypeService contentTypeService)
{
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants());
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants(contentTypeService));
return descendantsAndSelf;
}
/// <summary>
/// Get all descendant and self content types
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<IContentTypeBase> DescendantsAndSelf(this IContentTypeBase contentType, ContentTypeServiceBase contentTypeService)
{
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants(contentTypeService));
return descendantsAndSelf;
}
@@ -13,6 +13,7 @@ namespace Umbraco.Core.Models.Identity
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
{
config.CreateMap<IUser, BackOfficeIdentityUser>()
.ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime()))
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
@@ -24,12 +24,17 @@ namespace Umbraco.Core.Models.Identity
///
/// </summary>
public IdentityUser()
{
{
this.Claims = new List<TClaim>();
this.Roles = new List<TRole>();
this.Logins = new List<TLogin>();
}
/// <summary>
/// Last login date
/// </summary>
public virtual DateTime? LastLoginDateUtc { get; set; }
/// <summary>
/// Email
///
@@ -106,12 +106,13 @@ namespace Umbraco.Core.Models.PublishedContent
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <param name="dataTypeDefinitionId">The datatype definition identifier.</param>
/// <param name="propertyEditorAlias">The property editor alias.</param>
/// <param name="initConverters">Generally used only for testing, in production this will always be true</param>
/// <remarks>
/// <para>The new published property type does not belong to a published content type.</para>
/// <para>The values of <paramref name="dataTypeDefinitionId"/> and <paramref name="propertyEditorAlias"/> are
/// assumed to be valid and consistent.</para>
/// </remarks>
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias)
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias, bool initConverters = true)
{
// ContentType
// - in unit tests, to be set by PublishedContentType when creating it
@@ -122,7 +123,8 @@ namespace Umbraco.Core.Models.PublishedContent
DataTypeId = dataTypeDefinitionId;
PropertyEditorAlias = propertyEditorAlias;
InitializeConverters();
if (initConverters)
InitializeConverters();
}
#endregion
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.Rdbms
internal class DataTypePreValueDto
{
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 6)]
[PrimaryKeyColumn(IdentitySeed = 10)]
public int Id { get; set; }
[Column("datatypeNodeId")]
@@ -22,6 +22,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("LoginName")]
[Length(1000)]
[Constraint(Default = "''")]
[Index(IndexTypes.NonClustered, Name = "IX_cmsMember_LoginName")]
public string LoginName { get; set; }
[Column("Password")]
+1
View File
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("path")]
[Length(150)]
[Index(IndexTypes.NonClustered, Name = "IX_umbracoNodePath")]
public string Path { get; set; }
[Column("sortOrder")]
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("parentId")]
[ForeignKey(typeof(NodeDto), Name = "FK_umbracoRelation_umbracoNode")]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelation_parentChildType", ForColumns = "parentId,childId,relType")]
public int ParentId { get; set; }
[Column("childId")]
@@ -29,11 +29,13 @@ namespace Umbraco.Core.Models.Rdbms
public Guid ChildObjectType { get; set; }
[Column("name")]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_name")]
public string Name { get; set; }
[Column("alias")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(100)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_alias")]
public string Alias { get; set; }
}
}
@@ -15,6 +15,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("nodeId")]
[ForeignKey(typeof(NodeDto))]
[Index(IndexTypes.NonClustered, Name = "IX_umbracoUser2NodePermission_nodeId")]
public int NodeId { get; set; }
[Column("permission")]
+24 -10
View File
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Models
/// <summary>
/// Root
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
[UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
[FriendlyName("Root")]
ROOT,
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Document, typeof(IContent))]
[FriendlyName("Document")]
[UmbracoUdiType(Constants.UdiEntityType.Document)]
Document,
/// <summary>
@@ -42,6 +43,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Media, typeof(IMedia))]
[FriendlyName("Media")]
[UmbracoUdiType(Constants.UdiEntityType.Media)]
Media,
/// <summary>
@@ -49,6 +51,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.MemberType, typeof(IMemberType))]
[FriendlyName("Member Type")]
[UmbracoUdiType(Constants.UdiEntityType.MemberType)]
MemberType,
/// <summary>
@@ -56,6 +59,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Template, typeof(ITemplate))]
[FriendlyName("Template")]
[UmbracoUdiType(Constants.UdiEntityType.Template)]
Template,
/// <summary>
@@ -63,6 +67,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.MemberGroup)]
[FriendlyName("Member Group")]
[UmbracoUdiType(Constants.UdiEntityType.MemberGroup)]
MemberGroup,
//TODO: What is a 'Content Item' supposed to be???
@@ -80,6 +85,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.MediaType, typeof(IMediaType))]
[FriendlyName("Media Type")]
[UmbracoUdiType(Constants.UdiEntityType.MediaType)]
MediaType,
/// <summary>
@@ -87,13 +93,14 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.DocumentType, typeof(IContentType))]
[FriendlyName("Document Type")]
[UmbracoUdiType(Constants.UdiEntityType.DocumentType)]
DocumentType,
/// <summary>
/// Recycle Bin
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.ContentRecycleBin)]
[FriendlyName("Recycle Bin")]
[FriendlyName("Recycle Bin")]
RecycleBin,
/// <summary>
@@ -101,6 +108,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Stylesheet)]
[FriendlyName("Stylesheet")]
[UmbracoUdiType(Constants.UdiEntityType.Stylesheet)]
Stylesheet,
/// <summary>
@@ -108,6 +116,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Member, typeof(IMember))]
[FriendlyName("Member")]
[UmbracoUdiType(Constants.UdiEntityType.Member)]
Member,
/// <summary>
@@ -115,6 +124,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.DataType, typeof(IDataTypeDefinition))]
[FriendlyName("Data Type")]
[UmbracoUdiType(Constants.UdiEntityType.DataType)]
DataType,
/// <summary>
@@ -122,6 +132,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.DocumentTypeContainer)]
[FriendlyName("Document Type Container")]
[UmbracoUdiType(Constants.UdiEntityType.DocumentTypeContainer)]
DocumentTypeContainer,
/// <summary>
@@ -129,6 +140,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.MediaTypeContainer)]
[FriendlyName("Media Type Container")]
[UmbracoUdiType(Constants.UdiEntityType.MediaTypeContainer)]
MediaTypeContainer,
/// <summary>
@@ -136,6 +148,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.DataTypeContainer)]
[FriendlyName("Data Type Container")]
[UmbracoUdiType(Constants.UdiEntityType.DataTypeContainer)]
DataTypeContainer,
/// <summary>
@@ -143,6 +156,7 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.RelationType)]
[FriendlyName("Relation Type")]
[UmbracoUdiType(Constants.UdiEntityType.RelationType)]
RelationType,
/// <summary>
@@ -153,17 +167,17 @@ namespace Umbraco.Core.Models
FormsForm,
/// <summary>
/// Forms Workflow
/// Forms PreValue
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.FormsWorkflow)]
[FriendlyName("Workflow")]
FormsWorkflow,
[UmbracoObjectType(Constants.ObjectTypes.FormsPreValue)]
[FriendlyName("PreValue")]
FormsPreValue,
/// <summary>
/// Forms Record
/// Forms DataSource
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.FormsRecord)]
[FriendlyName("Record")]
FormsRecord
[UmbracoObjectType(Constants.ObjectTypes.FormsDataSource)]
[FriendlyName("DataSource")]
FormsDataSource
}
}
@@ -12,6 +12,7 @@ namespace Umbraco.Core.Models
{
//MUST be concurrent to avoid thread collisions!
private static readonly ConcurrentDictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new ConcurrentDictionary<UmbracoObjectTypes, Guid>();
private static readonly ConcurrentDictionary<UmbracoObjectTypes, string> UmbracoObjectTypeUdiCache = new ConcurrentDictionary<UmbracoObjectTypes, string>();
/// <summary>
/// Get an UmbracoObjectTypes value from it's name
@@ -43,6 +44,21 @@ namespace Umbraco.Core.Models
return umbracoObjectType;
}
public static string GetUdiType(Guid guid)
{
var umbracoObjectType = Constants.UdiEntityType.Unknown;
foreach (var name in Enum.GetNames(typeof(UmbracoObjectTypes)))
{
var objType = GetUmbracoObjectType(name);
if (objType.GetGuid() == guid)
{
umbracoObjectType = GetUdiType(objType);
}
}
return umbracoObjectType;
}
/// <summary>
/// Extension method for the UmbracoObjectTypes enum to return the enum GUID
/// </summary>
@@ -68,6 +84,26 @@ namespace Umbraco.Core.Models
});
}
public static string GetUdiType(this UmbracoObjectTypes umbracoObjectType)
{
return UmbracoObjectTypeUdiCache.GetOrAdd(umbracoObjectType, types =>
{
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoUdiTypeAttribute),
false);
if (attributes.Length == 0)
return Constants.UdiEntityType.Unknown;
var attribute = ((UmbracoUdiTypeAttribute)attributes[0]);
if (attribute == null)
return Constants.UdiEntityType.Unknown;
return attribute.UdiType;
});
}
/// <summary>
/// Extension method for the UmbracoObjectTypes enum to return the enum name
/// </summary>
+5
View File
@@ -647,5 +647,10 @@ namespace Umbraco.Core
return "[GetPropertyValueException]";
}
}
internal static Guid AsGuid(this object value)
{
return value is Guid ? (Guid) value : Guid.Empty;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
using System.Collections.ObjectModel;
namespace Umbraco.Core
{
/// <summary>
/// A custom collection similar to HashSet{T} which only contains unique items, however this collection keeps items in order
/// and is customizable to keep the newest or oldest equatable item
/// </summary>
/// <typeparam name="T"></typeparam>
internal class OrderedHashSet<T> : KeyedCollection<T, T>
{
private readonly bool _keepOldest;
public OrderedHashSet(bool keepOldest = true)
{
_keepOldest = keepOldest;
}
protected override void InsertItem(int index, T item)
{
if (Dictionary == null)
{
base.InsertItem(index, item);
}
else
{
var exists = Dictionary.ContainsKey(item);
//if we want to keep the newest, then we need to remove the old item and add the new one
if (exists == false)
{
base.InsertItem(index, item);
}
else if(_keepOldest == false)
{
if (Remove(item))
{
index--;
}
base.InsertItem(index, item);
}
}
}
protected override T GetKeyForItem(T item)
{
return item;
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Umbraco.Core.Models;
namespace Umbraco.Core.Packaging.Models
{
[Serializable]
[DataContract(IsReference = true)]
internal class UninstallationSummary
{
public MetaData MetaData { get; set; }
public IEnumerable<IDataTypeDefinition> DataTypesUninstalled { get; set; }
public IEnumerable<ILanguage> LanguagesUninstalled { get; set; }
public IEnumerable<IDictionaryItem> DictionaryItemsUninstalled { get; set; }
public IEnumerable<IMacro> MacrosUninstalled { get; set; }
public IEnumerable<string> FilesUninstalled { get; set; }
public IEnumerable<ITemplate> TemplatesUninstalled { get; set; }
public IEnumerable<IContentType> ContentTypesUninstalled { get; set; }
public IEnumerable<IFile> StylesheetsUninstalled { get; set; }
public IEnumerable<IContent> ContentUninstalled { get; set; }
public bool PackageUninstalled { get; set; }
}
internal static class UninstallationSummaryExtentions
{
public static UninstallationSummary InitEmpty(this UninstallationSummary summary)
{
summary.ContentUninstalled = new List<IContent>();
summary.ContentTypesUninstalled = new List<IContentType>();
summary.DataTypesUninstalled = new List<IDataTypeDefinition>();
summary.DictionaryItemsUninstalled = new List<IDictionaryItem>();
summary.FilesUninstalled = new List<string>();
summary.LanguagesUninstalled = new List<ILanguage>();
summary.MacrosUninstalled = new List<IMacro>();
summary.MetaData = new MetaData();
summary.TemplatesUninstalled = new List<ITemplate>();
summary.PackageUninstalled = false;
return summary;
}
}
}
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using System.Web;
using Umbraco.Core.Logging;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Persistence
{
@@ -14,30 +12,17 @@ namespace Umbraco.Core.Persistence
/// it will create one per context, otherwise it will be a global singleton object which is NOT thread safe
/// since we need (at least) a new instance of the database object per thread.
/// </remarks>
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory2
{
private readonly string _connectionStringName;
private readonly ILogger _logger;
public string ConnectionString { get; private set; }
public string ProviderName { get; private set; }
// NO! see notes in v8 HybridAccessorBase
//[ThreadStatic]
//private static volatile UmbracoDatabase _nonHttpInstance;
//private static readonly object Locker = new object();
private const string ItemKey = "Umbraco.Core.Persistence.DefaultDatabaseFactory";
private static UmbracoDatabase NonContextValue
{
get { return (UmbracoDatabase) CallContext.LogicalGetData(ItemKey); }
set
{
if (value == null) CallContext.FreeNamedDataSlot(ItemKey);
else CallContext.LogicalSetData(ItemKey, value);
}
}
private static readonly object Locker = new object();
// bwc imposes a weird x-dependency between database factory and scope provider...
public IScopeProviderInternal ScopeProvider { get; set; }
/// <summary>
/// Constructor accepting custom connection string
@@ -76,251 +61,26 @@ namespace Umbraco.Core.Persistence
public UmbracoDatabase CreateDatabase()
{
UmbracoDatabase database;
// gets or creates a database, using either the call context (if no http context) or
// the current request context (http context) to store it. once done using the database,
// it should be disposed - which will remove it from whatever context it is currently
// stored in. this is automatic with http context because UmbracoDatabase implements
// IDisposeOnRequestEnd, but NOT with call context.
if (HttpContext.Current == null)
{
database = NonContextValue;
if (database == null)
{
lock (Locker)
{
database = NonContextValue;
if (database == null)
{
database = CreateDatabaseInstance(ContextOwner.CallContext);
NonContextValue = database;
}
#if DEBUG_DATABASES
else
{
Log("Get lcc", database);
}
#endif
}
}
#if DEBUG_DATABASES
else
{
Log("Get lcc", database);
}
#endif
return database;
}
if (HttpContext.Current.Items.Contains(typeof (DefaultDatabaseFactory)) == false)
{
database = CreateDatabaseInstance(ContextOwner.HttpContext);
HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory), database);
}
else
{
database = (UmbracoDatabase) HttpContext.Current.Items[typeof(DefaultDatabaseFactory)];
#if DEBUG_DATABASES
Log("Get ctx", database);
#endif
}
return database;
return ScopeProvider.GetAmbientOrNoScope().Database;
}
// called by UmbracoDatabase when disposed, so that the factory can de-list it from context
internal void OnDispose(UmbracoDatabase disposing)
public UmbracoDatabase CreateNewDatabase()
{
var value = disposing;
switch (disposing.ContextOwner)
{
case ContextOwner.CallContext:
value = NonContextValue;
break;
case ContextOwner.HttpContext:
value = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)];
break;
}
return CreateDatabaseInstance();
if (value != null && value.InstanceId != disposing.InstanceId) throw new Exception("panic: wrong db.");
}
switch (disposing.ContextOwner)
{
case ContextOwner.CallContext:
NonContextValue = null;
#if DEBUG_DATABASES
Log("Clr lcc", disposing);
#endif
break;
case ContextOwner.HttpContext:
HttpContext.Current.Items.Remove(typeof(DefaultDatabaseFactory));
#if DEBUG_DATABASES
Log("Clr ctx", disposing);
#endif
break;
}
disposing.ContextOwner = ContextOwner.None;
#if DEBUG_DATABASES
_databases.Remove(value);
#endif
}
#if DEBUG_DATABASES
// helps identifying when non-httpContext databases are created by logging the stack trace
private void LogCallContextStack()
{
var trace = Environment.StackTrace;
if (trace.IndexOf("ScheduledPublishing") > 0)
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: Scheduled Publishing");
else if (trace.IndexOf("TouchServerTask") > 0)
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: Server Registration");
else if (trace.IndexOf("LogScrubber") > 0)
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: Log Scrubber");
else
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: " + Environment.StackTrace);
}
private readonly List<UmbracoDatabase> _databases = new List<UmbracoDatabase>();
// helps identifying database leaks by keeping track of all instances
public List<UmbracoDatabase> Databases { get { return _databases; } }
private static void Log(string message, UmbracoDatabase database)
{
LogHelper.Debug<DefaultDatabaseFactory>(message + " (" + (database == null ? "" : database.InstanceSid) + ").");
}
#endif
internal enum ContextOwner
{
None,
HttpContext,
CallContext
}
internal UmbracoDatabase CreateDatabaseInstance(ContextOwner contextOwner)
internal UmbracoDatabase CreateDatabaseInstance()
{
var database = string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
? new UmbracoDatabase(ConnectionString, ProviderName, _logger)
: new UmbracoDatabase(_connectionStringName, _logger);
database.ContextOwner = contextOwner;
database.DatabaseFactory = this;
//database.EnableSqlTrace = true;
#if DEBUG_DATABASES
Log("Create " + contextOwner, database);
if (contextOwner == ContextOwner.CallContext)
LogCallContextStack();
_databases.Add(database);
#endif
return database;
}
protected override void DisposeResources()
{
UmbracoDatabase database;
if (HttpContext.Current == null)
{
database = NonContextValue;
#if DEBUG_DATABASES
Log("Release lcc", database);
#endif
}
else
{
database = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)];
#if DEBUG_DATABASES
Log("Release ctx", database);
#endif
}
if (database != null) database.Dispose(); // removes it from call context
}
// during tests, the thread static var can leak between tests
// this method provides a way to force-reset the variable
internal void ResetForTests()
{
var value = NonContextValue;
if (value != null) value.Dispose();
NonContextValue = null;
}
#region SafeCallContext
// see notes in SafeCallContext - need to do this since we are using
// the logical call context...
static DefaultDatabaseFactory()
{
SafeCallContext.Register(DetachAmbientDatabase, AttachAmbientDatabase);
}
// gets a value indicating whether there is an ambient database
internal static bool HasAmbientDatabase
{
get
{
return HttpContext.Current == null
? NonContextValue != null
: HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] != null;
}
}
// detaches the current database
// ie returns the database and remove it from whatever is "context"
internal static UmbracoDatabase DetachAmbientDatabase()
{
UmbracoDatabase database;
if (HttpContext.Current == null)
{
database = NonContextValue;
NonContextValue = null;
}
else
{
database = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)];
HttpContext.Current.Items.Remove(typeof (DefaultDatabaseFactory));
}
if (database != null) database.ContextOwner = ContextOwner.None;
return database;
}
// attach a current database
// ie assign it to whatever is "context"
// throws if there already is a database
internal static void AttachAmbientDatabase(object o)
{
var database = o as UmbracoDatabase;
if (o != null && database == null) throw new ArgumentException("Not an UmbracoDatabase.", "o");
var ambient = DetachAmbientDatabase();
if (ambient != null) ambient.Dispose();
if (HttpContext.Current == null)
{
//if (NonContextValue != null) throw new InvalidOperationException();
if (database == null) return;
NonContextValue = database;
database.ContextOwner = ContextOwner.CallContext;
}
else
{
//if (HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] != null) throw new InvalidOperationException();
if (database == null) return;
HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] = database;
database.ContextOwner = ContextOwner.HttpContext;
}
}
#endregion
}
}
@@ -27,7 +27,20 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory<IContent,DocumentDto>
public static IContent BuildEntity(DocumentDto dto, IContentType contentType)
/// <summary>
/// Builds a IContent item from the dto(s) and content type
/// </summary>
/// <param name="dto">
/// This DTO can contain all of the information to build an IContent item, however in cases where multiple entities are being built,
/// a separate <see cref="DocumentPublishedReadOnlyDto"/> publishedDto entity will be supplied in place of the <see cref="DocumentDto"/>'s own
/// ResultColumn DocumentPublishedReadOnlyDto
/// </param>
/// <param name="contentType"></param>
/// <param name="publishedDto">
/// When querying for multiple content items the main DTO will not contain the ResultColumn DocumentPublishedReadOnlyDto and a separate publishedDto instance will be supplied
/// </param>
/// <returns></returns>
public static IContent BuildEntity(DocumentDto dto, IContentType contentType, DocumentPublishedReadOnlyDto publishedDto = null)
{
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType);
@@ -52,8 +65,13 @@ namespace Umbraco.Core.Persistence.Factories
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null;
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null;
content.Version = dto.ContentVersionDto.VersionId;
content.PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished;
content.PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId;
//Check if the publishedDto has been supplied, if not the use the dto's own DocumentPublishedReadOnlyDto value
content.PublishedVersionGuid = publishedDto == null
? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId)
: publishedDto.VersionId;
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -1,12 +1,30 @@
using System;
using System.ComponentModel;
namespace Umbraco.Core.Persistence
{
/// <summary>
/// Used to create the UmbracoDatabase for use in the DatabaseContext
/// </summary>
[Obsolete("Use IDatabaseFactory2 instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IDatabaseFactory : IDisposable
{
/// <summary>
/// gets or creates the ambient database
/// </summary>
/// <returns></returns>
UmbracoDatabase CreateDatabase();
}
/// <summary>
/// Used to create the UmbracoDatabase for use in the DatabaseContext
/// </summary>
#pragma warning disable 618
public interface IDatabaseFactory2 : IDatabaseFactory
#pragma warning restore 618
{
/// <summary>
/// creates a new database
/// </summary>
/// <returns></returns>
UmbracoDatabase CreateNewDatabase();
}
}
@@ -7,21 +7,27 @@ namespace Umbraco.Core.Persistence
internal class LockedRepository<TRepository>
where TRepository : IDisposable, IRepository
{
public LockedRepository(Transaction transaction, IDatabaseUnitOfWork unitOfWork, TRepository repository)
public LockedRepository(IDatabaseUnitOfWork unitOfWork, TRepository repository)
{
Transaction = transaction;
UnitOfWork = unitOfWork;
Repository = repository;
}
public Transaction Transaction { get; private set; }
public LockedRepository(Transaction transaction, IDatabaseUnitOfWork unitOfWork, TRepository repository)
{
//Transaction = transaction;
UnitOfWork = unitOfWork;
Repository = repository;
}
//public Transaction Transaction { get; private set; }
public IDatabaseUnitOfWork UnitOfWork { get; private set; }
public TRepository Repository { get; private set; }
public void Commit()
{
UnitOfWork.Commit();
Transaction.Complete();
}
//public void Commit()
//{
// UnitOfWork.Commit();
// Transaction.Complete();
//}
}
}
@@ -10,11 +10,11 @@ namespace Umbraco.Core.Persistence
internal class LockingRepository<TRepository>
where TRepository : IDisposable, IRepository
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly Func<IDatabaseUnitOfWork, TRepository> _repositoryFactory;
private readonly IScopeUnitOfWorkProvider _uowProvider;
private readonly Func<IScopeUnitOfWork, TRepository> _repositoryFactory;
private readonly int[] _readLockIds, _writeLockIds;
public LockingRepository(IDatabaseUnitOfWorkProvider uowProvider, Func<IDatabaseUnitOfWork, TRepository> repositoryFactory,
public LockingRepository(IScopeUnitOfWorkProvider uowProvider, Func<IScopeUnitOfWork, TRepository> repositoryFactory,
IEnumerable<int> readLockIds, IEnumerable<int> writeLockIds)
{
Mandate.ParameterNotNull(uowProvider, "uowProvider");
@@ -28,76 +28,88 @@ namespace Umbraco.Core.Persistence
public void WithReadLocked(Action<LockedRepository<TRepository>> action, bool autoCommit = true)
{
var uow = _uowProvider.GetUnitOfWork();
using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead))
{
// getting the database creates a scope and a transaction
// the scope is IsolationLevel.RepeatableRead (because UnitOfWork is)
// and will throw if outer scope (if any) has a lower isolation level
foreach (var lockId in _readLockIds)
uow.Database.AcquireLockNodeReadLock(lockId);
using (var repository = _repositoryFactory(uow))
{
action(new LockedRepository<TRepository>(transaction, uow, repository));
action(new LockedRepository<TRepository>(uow, repository));
if (autoCommit == false) return;
uow.Commit();
transaction.Complete();
}
}
} // dispose repository => dispose uow => complete (or not) scope
} // dispose uow again => nothing
}
public TResult WithReadLocked<TResult>(Func<LockedRepository<TRepository>, TResult> func, bool autoCommit = true)
{
var uow = _uowProvider.GetUnitOfWork();
using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead))
{
// getting the database creates a scope and a transaction
// the scope is IsolationLevel.RepeatableRead (because UnitOfWork is)
// and will throw if outer scope (if any) has a lower isolation level
foreach (var lockId in _readLockIds)
uow.Database.AcquireLockNodeReadLock(lockId);
using (var repository = _repositoryFactory(uow))
{
var ret = func(new LockedRepository<TRepository>(transaction, uow, repository));
var ret = func(new LockedRepository<TRepository>(uow, repository));
if (autoCommit == false) return ret;
uow.Commit();
transaction.Complete();
return ret;
}
}
} // dispose repository => dispose uow => complete (or not) scope
} // dispose uow again => nothing
}
public void WithWriteLocked(Action<LockedRepository<TRepository>> action, bool autoCommit = true)
{
var uow = _uowProvider.GetUnitOfWork();
using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead))
{
// getting the database creates a scope and a transaction
// the scope is IsolationLevel.RepeatableRead (because UnitOfWork is)
// and will throw if outer scope (if any) has a lower isolation level
foreach (var lockId in _writeLockIds)
uow.Database.AcquireLockNodeWriteLock(lockId);
using (var repository = _repositoryFactory(uow))
{
action(new LockedRepository<TRepository>(transaction, uow, repository));
action(new LockedRepository<TRepository>(uow, repository));
if (autoCommit == false) return;
uow.Commit();
transaction.Complete();
}
}
} // dispose repository => dispose uow => complete (or not) scope
} // dispose uow again => nothing
}
public TResult WithWriteLocked<TResult>(Func<LockedRepository<TRepository>, TResult> func, bool autoCommit = true)
{
var uow = _uowProvider.GetUnitOfWork();
using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead))
{
// getting the database creates a scope and a transaction
// the scope is IsolationLevel.RepeatableRead (because UnitOfWork is)
// and will throw if outer scope (if any) has a lower isolation level
foreach (var lockId in _writeLockIds)
uow.Database.AcquireLockNodeReadLock(lockId);
using (var repository = _repositoryFactory(uow))
{
var ret = func(new LockedRepository<TRepository>(transaction, uow, repository));
var ret = func(new LockedRepository<TRepository>(uow, repository));
if (autoCommit == false) return ret;
uow.Commit();
transaction.Complete();
return ret;
}
}
} // dispose repository => dispose uow => complete (or not) scope
} // dispose uow again => nothing
}
}
}

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