Compare commits

...

129 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
Mads Rasmussen c060856782 fix breadcrumb for pickers with a startNode 2017-02-08 11:09:09 +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
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 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
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
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 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 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
Richard Soeteman a3c7b1fcfe Fixed issue U4-9494 2017-02-06 11:51:44 +01:00
Richard Soeteman 02785eea42 Fixed issue U4-9493 2017-02-06 11:26:58 +01: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 db86f6e1ee Fixes up issues reported on review and adds a null check 2017-02-03 11:49:22 +11:00
Shannon a03e0dfb53 Fixes tests 2017-02-02 22:21:36 +11: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
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
Mads Rasmussen c82bddb7dd remove fake list view search node 2017-02-02 09:51:13 +01:00
Mads Rasmussen f2fd45576b speed up animations 2017-02-02 09:35:01 +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
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
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
Mads Rasmussen 26acecb720 start on animation and nested list views 2017-02-01 00:28:40 +01: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
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 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
Mads Rasmussen ed019a90e2 start on mini list view for pickers 2017-01-30 14:25:10 +01:00
Shannon 8d598cd37c Gets EntityController working with UDIs, INT and GUID for GetByIds 2017-01-30 20:40:45 +11: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 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
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
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
167 changed files with 4738 additions and 1595 deletions
+31 -31
View File
@@ -44,37 +44,37 @@
</dependencies>
</metadata>
<files>
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\net462\businesslogic.dll" />
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\net462\businesslogic.xml" />
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\net462\cms.dll" />
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\net462\cms.xml" />
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\net462\controls.dll" />
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\net462\controls.xml" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\net462\interfaces.dll" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\net462\interfaces.xml" />
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\net462\log4net.dll" />
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net462\Microsoft.ApplicationBlocks.Data.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net462\SQLCE4Umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net462\SQLCE4Umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net462\System.Data.SqlServerCe.dll" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net462\System.Data.SqlServerCe.Entity.dll" />
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\net462\TidyNet.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\net462\Umbraco.Core.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\net462\Umbraco.Core.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\net462\umbraco.DataLayer.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\net462\umbraco.DataLayer.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\net462\umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\net462\umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\net462\umbraco.editorControls.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\net462\umbraco.editorControls.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net462\umbraco.MacroEngines.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net462\umbraco.MacroEngines.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\net462\umbraco.providers.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\net462\umbraco.providers.xml" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net462\Umbraco.Web.UI.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net462\Umbraco.Web.UI.xml" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\net462\UmbracoExamine.dll" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\net462\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 -->
+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
alpha060
alpha064
+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-alpha060")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha064")]
@@ -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 "alpha060"; } }
public static string CurrentComment { get { return "alpha064"; } }
// 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.
+1 -1
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Umbraco.Core.Events
{
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<DeleteEventArgs<TEntity>>
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<DeleteEventArgs<TEntity>>, IDeletingMediaFilesEventArgs
{
/// <summary>
/// Constructor accepting multiple entities that are used in the delete operation
@@ -1,29 +0,0 @@
namespace Umbraco.Core.Events
{
public enum EventsDispatchMode
{
// in 7.5 we'd do:
//
// using (var uow = ...)
// { ... }
// Done.RaiseEvent(...);
//
// and so the event would trigger only *after* the transaction has completed,
// so actually PassThrough is more aggressive than what we had in 7.5 and should
// not be used. now in 7.6 we do:
//
// using (var uow = ...)
// {
// ...
// uow.Events.Dispatch(Done, ...);
// }
//
// so the event can be collected, so the default "kinda compatible" more has to be
// the Scope mode, and Passive is for Deploy only
Unspecified = 0,
PassThrough, // both Doing and Done trigger immediately
Scope, // Doing triggers immediately, Done queued and triggered when & if the scope completes
Passive // Doing never triggers, Done queued and needs to be handled by custom code
}
}
@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Umbraco.Core.Events
{
internal interface IDeletingMediaFilesEventArgs
{
List<string> MediaFilesToDelete { get; }
}
}
@@ -23,6 +23,11 @@ namespace Umbraco.Core.Events
{
get { return _packageMetaData; }
}
public IEnumerable<TEntity> InstallationSummary
{
get { return EventObject; }
}
public bool Equals(ImportPackageEventArgs<TEntity> other)
{
@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Events
{
public class RecycleBinEventArgs : CancellableEventArgs, IEquatable<RecycleBinEventArgs>
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>
+31 -109
View File
@@ -1,126 +1,48 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
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 : IEventDispatcher
internal class ScopeEventDispatcher : ScopeEventDispatcherBase
{
private readonly EventsDispatchMode _mode;
private List<IEventDefinition> _events;
public ScopeEventDispatcher()
: base(true)
{ }
public ScopeEventDispatcher(EventsDispatchMode mode)
{
_mode = mode;
}
private List<IEventDefinition> Events { get { return _events ?? (_events = new List<IEventDefinition>()); } }
private bool PassThroughCancelable { get { return _mode == EventsDispatchMode.PassThrough || _mode == EventsDispatchMode.Scope; } }
private bool PassThrough { get { return _mode == EventsDispatchMode.PassThrough; } }
private bool RaiseEvents { get { return _mode == EventsDispatchMode.Scope; } }
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
{
if (eventHandler == null) return args.Cancel;
if (PassThroughCancelable == 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 (PassThroughCancelable == 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 (PassThroughCancelable == 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;
if (PassThrough)
eventHandler(sender, args);
else
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;
if (PassThrough)
eventHandler(sender, args);
else
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;
if (PassThrough)
eventHandler(sender, args);
else
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)
protected override void ScopeExitCompleted()
{
// fixme - we'd need to de-duplicate events somehow, etc - and the deduplication should be last in wins
if (_events == null) return;
foreach (var e in GetEvents(EventDefinitionFilter.All))
{
e.RaiseEvent();
if (RaiseEvents && completed)
foreach (var e in _events)
e.RaiseEvent();
_events.Clear();
// 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,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; }
}
}
}
+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
@@ -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
///
@@ -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")]
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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
@@ -130,21 +130,21 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = Constants.System.DefaultMembersListViewDataTypeId, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-97", SortOrder = 2, UniqueId = new Guid("AA2C52A0-CE87-4E65-A47C-7DF09358585D"), Text = Constants.Conventions.DataTypes.ListViewPrefix + "Members", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1031, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1031", SortOrder = 2, UniqueId = new Guid("f38bd2d7-65d0-48e6-95dc-87ce06ec2d3d"), Text = Constants.Conventions.MediaTypes.Folder, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1032, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1032", SortOrder = 2, UniqueId = new Guid("cc07b313-0843-4aa8-bbda-871c8da728c8"), Text = Constants.Conventions.MediaTypes.Image, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"), Text = Constants.Conventions.MediaTypes.File, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1034, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1034", SortOrder = 2, UniqueId = new Guid("a6857c73-d6e9-480c-b6e6-f15f6ad11125"), Text = "Content Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1035, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1035", SortOrder = 2, UniqueId = new Guid("93929b9a-93a2-4e2a-b239-d99334440a59"), Text = "Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = new Guid("2b24165f-9782-4aa3-b459-1de4a4d21f60"), Text = "Member Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"), Text = Constants.Conventions.MediaTypes.File, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1040, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1040", SortOrder = 2, UniqueId = new Guid("21e798da-e06e-4eda-a511-ed257f78d4fa"), Text = "Related Links", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1041, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1041", SortOrder = 2, UniqueId = new Guid("b6b73142-b9c1-4bf8-a16d-e1c23320b549"), Text = "Tags", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1043, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1043", SortOrder = 2, UniqueId = new Guid("1df9f033-e6d4-451f-b8d2-e0cbc50a836f"), Text = "Image Cropper", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1045, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1045", SortOrder = 2, UniqueId = new Guid("7E3962CC-CE20-4FFC-B661-5897A894BA7E"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//New UDI pickers with newer Ids
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//TODO: We're not creating these for 7.0
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1039, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1039", SortOrder = 2, UniqueId = new Guid("06f349a9-c949-4b6a-8660-59c10451af42"), Text = "Ultimate Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1038, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1038", SortOrder = 2, UniqueId = new Guid("1251c96c-185c-4e9b-93f4-b48205573cbd"), Text = "Simple Editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1042, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1042", SortOrder = 2, UniqueId = new Guid("0a452bd5-83f9-4bc3-8403-1286e13fb77e"), Text = "Macro Container", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
}
@@ -246,17 +246,19 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -40, PropertyEditorAlias = Constants.PropertyEditors.RadioButtonListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, PropertyEditorAlias = Constants.PropertyEditors.DateAlias, DbType = "Date" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 14, DataTypeId = -42, PropertyEditorAlias = Constants.PropertyEditors.DropDownListAlias, DbType = "Integer" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, PropertyEditorAlias = Constants.PropertyEditors.CheckBoxListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 16, DataTypeId = 1034, PropertyEditorAlias = Constants.PropertyEditors.ContentPickerAlias, DbType = "Integer" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 18, DataTypeId = 1036, PropertyEditorAlias = Constants.PropertyEditors.MemberPickerAlias, DbType = "Integer" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 17, DataTypeId = 1035, PropertyEditorAlias = Constants.PropertyEditors.MultipleMediaPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, PropertyEditorAlias = Constants.PropertyEditors.CheckBoxListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 21, DataTypeId = 1040, PropertyEditorAlias = Constants.PropertyEditors.RelatedLinksAlias, DbType = "Ntext" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 22, DataTypeId = 1041, PropertyEditorAlias = Constants.PropertyEditors.TagsAlias, DbType = "Ntext" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 24, DataTypeId = 1043, PropertyEditorAlias = Constants.PropertyEditors.ImageCropperAlias, DbType = "Ntext" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 25, DataTypeId = 1045, PropertyEditorAlias = Constants.PropertyEditors.MultipleMediaPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -26, DataTypeId = Constants.System.DefaultContentListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -27, DataTypeId = Constants.System.DefaultMediaListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -28, DataTypeId = Constants.System.DefaultMembersListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -28, DataTypeId = Constants.System.DefaultMembersListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
//New UDI pickers with newer Ids
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 26, DataTypeId = 1046, PropertyEditorAlias = Constants.PropertyEditors.ContentPicker2Alias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 27, DataTypeId = 1047, PropertyEditorAlias = Constants.PropertyEditors.MemberPicker2Alias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 28, DataTypeId = 1048, PropertyEditorAlias = Constants.PropertyEditors.MediaPicker2Alias, DbType = "Ntext" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 29, DataTypeId = 1049, PropertyEditorAlias = Constants.PropertyEditors.MediaPicker2Alias, DbType = "Ntext" });
//TODO: We're not creating these for 7.0
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 19, DataTypeId = 1038, PropertyEditorAlias = Constants.PropertyEditors.MarkdownEditorAlias, DbType = "Ntext" });
@@ -268,10 +270,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 3, Alias = "", SortOrder = 0, DataTypeNodeId = -87, Value = ",code,undo,redo,cut,copy,mcepasteword,stylepicker,bold,italic,bullist,numlist,outdent,indent,mcelink,unlink,mceinsertanchor,mceimage,umbracomacro,mceinserttable,umbracoembed,mcecharmap,|1|1,2,3,|0|500,400|1049,|true|" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 4, Alias = "group", SortOrder = 0, DataTypeNodeId = 1041, Value = "default" });
//default's for MultipleMediaPickerAlias picker
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 5, Alias = "multiPicker", SortOrder = 0, DataTypeNodeId = 1045, Value = "1" });
//defaults for the member list
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -1, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "10" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "username" });
@@ -288,6 +287,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -7, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "desc" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -8, Alias = "layouts", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[" + cardLayout + "," + listLayout + "]" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
//default's for MultipleMediaPickerAlias picker
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 6, Alias = "multiPicker", SortOrder = 0, DataTypeNodeId = 1049, Value = "1" });
}
private void CreateUmbracoRelationTypeData()
@@ -0,0 +1,35 @@
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
public class AddIndexToCmsMemberLoginName : MigrationBase
{
public AddIndexToCmsMemberLoginName(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMember_LoginName")) == false)
{
Create.Index("IX_cmsMember_LoginName").OnTable("cmsMember")
.OnColumn("LoginName")
.Ascending()
.WithOptions()
.NonClustered();
}
}
public override void Down()
{
Delete.Index("IX_cmsMember_LoginName").OnTable("cmsMember");
}
}
}
@@ -14,14 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
public override void Up()
{
var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
.Select(x => new DbIndexDefinition()
{
TableName = x.Item1,
IndexName = x.Item2,
ColumnName = x.Item3,
IsUnique = x.Item4
}).ToArray();
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodePath")) == false)
@@ -0,0 +1,34 @@
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
public class AddIndexToUser2NodePermission : MigrationBase
{
public AddIndexToUser2NodePermission(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoUser2NodePermission_nodeId")) == false)
{
Create.Index("IX_umbracoUser2NodePermission_nodeId").OnTable("umbracoUser2NodePermission")
.OnColumn("nodeId")
.Ascending()
.WithOptions()
.NonClustered();
}
}
public override void Down()
{
Delete.Index("IX_umbracoUser2NodePermission_nodeId").OnTable("cmsMember");
}
}
}
@@ -0,0 +1,91 @@
using System;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
public class AddIndexesToUmbracoRelationTables : MigrationBase
{
public AddIndexesToUmbracoRelationTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database).ToArray();
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoRelation_parentChildType")) == false)
{
//This will remove any corrupt/duplicate data in the relation table before the index is applied
//Ensure this executes in a defered block which will be done inside of the migration transaction
this.Execute.Code(database =>
{
//We need to check if this index has corrupted data and then clear that data
var duplicates = database.Fetch<dynamic>("SELECT parentId,childId,relType FROM umbracoRelation GROUP BY parentId,childId,relType HAVING COUNT(*) > 1");
if (duplicates.Count > 0)
{
//need to fix this there cannot be duplicates so we'll take the latest entries, it's really not going to matter though
foreach (var duplicate in duplicates)
{
var ids = database.Fetch<int>("SELECT id FROM umbracoRelation WHERE parentId=@parentId AND childId=@childId AND relType=@relType ORDER BY datetime DESC",
new { parentId = duplicate.parentId, childId = duplicate.childId, relType = duplicate.relType });
if (ids.Count == 1)
{
//this is just a safety check, this should absolutely never happen
throw new InvalidOperationException("Duplicates were detected but could not be discovered");
}
//delete the others
ids = ids.Skip(0).ToList();
//iterate in groups of 2000 to avoid the max sql parameter limit
foreach (var idGroup in ids.InGroupsOf(2000))
{
database.Execute("DELETE FROM umbracoRelation WHERE id IN (@ids)", new { ids = idGroup });
}
}
}
return "";
});
//unique index to prevent duplicates - and for better perf
Create.Index("IX_umbracoRelation_parentChildType").OnTable("umbracoRelation")
.OnColumn("parentId").Ascending()
.OnColumn("childId").Ascending()
.OnColumn("relType").Ascending()
.WithOptions()
.Unique();
}
//need indexes on alias and name for relation type since these are queried against
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoRelationType_alias")) == false)
{
Create.Index("IX_umbracoRelationType_alias").OnTable("umbracoRelationType")
.OnColumn("alias")
.Ascending()
.WithOptions()
.NonClustered();
}
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoRelationType_name")) == false)
{
Create.Index("IX_umbracoRelationType_name").OnTable("umbracoRelationType")
.OnColumn("name")
.Ascending()
.WithOptions()
.NonClustered();
}
}
public override void Down()
{
Delete.Index("IX_umbracoNodePath").OnTable("umbracoNode");
}
}
}
@@ -105,8 +105,8 @@ namespace Umbraco.Core.Persistence
// try to update
var rowCount = updateCommand.IsNullOrWhiteSpace()
? db.Update(poco)
: db.Update<T>(updateCommand, updateArgs);
? db.Update(poco)
: db.Update<T>(updateCommand, updateArgs);
if (rowCount > 0)
return RecordPersistenceType.Update;
@@ -162,7 +162,7 @@ namespace Umbraco.Core.Persistence
[Obsolete("Use the DatabaseSchemaHelper instead")]
public static void CreateTable<T>(this Database db)
where T : new()
where T : new()
{
var creator = new DatabaseSchemaHelper(db, LoggerResolver.Current.Logger, SqlSyntaxContext.SqlSyntaxProvider);
creator.CreateTable<T>();
@@ -170,7 +170,7 @@ namespace Umbraco.Core.Persistence
[Obsolete("Use the DatabaseSchemaHelper instead")]
public static void CreateTable<T>(this Database db, bool overwrite)
where T : new()
where T : new()
{
var creator = new DatabaseSchemaHelper(db, LoggerResolver.Current.Logger, SqlSyntaxContext.SqlSyntaxProvider);
creator.CreateTable<T>(overwrite);
@@ -231,6 +231,24 @@ namespace Umbraco.Core.Persistence
ISqlSyntaxProvider syntaxProvider,
bool useNativeSqlPlatformBulkInsert = true,
bool commitTrans = false)
{
db.OpenSharedConnection();
try
{
return BulkInsertRecordsTry(db, collection, tr, syntaxProvider, useNativeSqlPlatformBulkInsert, commitTrans);
}
finally
{
db.CloseSharedConnection();
}
}
public static int BulkInsertRecordsTry<T>(this Database db,
IEnumerable<T> collection,
Transaction tr,
ISqlSyntaxProvider syntaxProvider,
bool useNativeSqlPlatformBulkInsert = true,
bool commitTrans = false)
{
if (commitTrans && tr == null)
throw new ArgumentNullException("tr", "The transaction cannot be null if commitTrans is true.");
@@ -241,15 +259,15 @@ namespace Umbraco.Core.Persistence
return 0;
}
var pd = Database.PocoData.ForType(typeof(T));
if (pd == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T));
var pd = Database.PocoData.ForType(typeof (T));
if (pd == null) throw new InvalidOperationException("Could not find PocoData for " + typeof (T));
try
{
int processed = 0;
var usedNativeSqlPlatformInserts = useNativeSqlPlatformBulkInsert
&& NativeSqlPlatformBulkInsertRecords(db, syntaxProvider, pd, collection, out processed);
&& NativeSqlPlatformBulkInsertRecords(db, syntaxProvider, pd, collection, out processed);
if (usedNativeSqlPlatformInserts == false)
{
@@ -283,19 +301,13 @@ namespace Umbraco.Core.Persistence
}
if (commitTrans)
{
if (tr == null) throw new ArgumentNullException("The transaction cannot be null if commitTrans is true");
tr.Complete();
}
return processed;
}
catch
{
if (commitTrans)
{
if (tr == null) throw new ArgumentNullException("The transaction cannot be null if commitTrans is true");
tr.Dispose();
}
throw;
}
@@ -337,13 +349,16 @@ namespace Umbraco.Core.Persistence
IEnumerable<T> collection,
out string[] sql)
{
if (db == null) throw new ArgumentNullException("db");
if (db.Connection == null) throw new ArgumentException("db.Connection is null.");
var tableName = db.EscapeTableName(pd.TableInfo.TableName);
//get all columns to include and format for sql
var cols = string.Join(", ",
pd.Columns
.Where(c => IncludeColumn(pd, c))
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
.Where(c => IncludeColumn(pd, c))
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
var itemArray = collection.ToArray();
@@ -357,9 +372,9 @@ namespace Umbraco.Core.Persistence
// 4168 / 262 = 15.908... = there will be 16 trans in total
//all items will be included if we have disabled db parameters
var itemsPerTrans = Math.Floor(2000.00 / paramsPerItem);
var itemsPerTrans = Math.Floor(2000.00/paramsPerItem);
//there will only be one transaction if we have disabled db parameters
var numTrans = Math.Ceiling(itemArray.Length / itemsPerTrans);
var numTrans = Math.Ceiling(itemArray.Length/itemsPerTrans);
var sqlQueries = new List<string>();
var commands = new List<IDbCommand>();
@@ -367,8 +382,8 @@ namespace Umbraco.Core.Persistence
for (var tIndex = 0; tIndex < numTrans; tIndex++)
{
var itemsForTrans = itemArray
.Skip(tIndex * (int)itemsPerTrans)
.Take((int)itemsPerTrans);
.Skip(tIndex*(int) itemsPerTrans)
.Take((int) itemsPerTrans);
var cmd = db.CreateCommand(db.Connection, string.Empty);
var pocoValues = new List<string>();
@@ -418,7 +433,6 @@ namespace Umbraco.Core.Persistence
/// <param name="processed">The number of records inserted</param>
private static bool NativeSqlPlatformBulkInsertRecords<T>(Database db, ISqlSyntaxProvider syntaxProvider, Database.PocoData pd, IEnumerable<T> collection, out int processed)
{
var dbConnection = db.Connection;
//unwrap the profiled connection if there is one
@@ -447,7 +461,6 @@ namespace Umbraco.Core.Persistence
//could not use the SQL server's specific bulk insert operations
processed = 0;
return false;
}
/// <summary>
@@ -2,8 +2,30 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal enum BaseQueryType
{
Full,
/// <summary>
/// A query to return all information for a single item
/// </summary>
/// <remarks>
/// In some cases this will be the same as <see cref="FullMultiple"/>
/// </remarks>
FullSingle,
/// <summary>
/// A query to return all information for multiple items
/// </summary>
/// <remarks>
/// In some cases this will be the same as <see cref="FullSingle"/>
/// </remarks>
FullMultiple,
/// <summary>
/// A query to return the ids for items
/// </summary>
Ids,
/// <summary>
/// A query to return the count for items
/// </summary>
Count
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Xml;
@@ -53,7 +54,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IContent PerformGet(int id)
{
var sql = GetBaseQuery(false)
var sql = GetBaseQuery(BaseQueryType.FullSingle)
.Where(GetBaseWhereClause(), new { Id = id })
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
@@ -81,15 +82,15 @@ namespace Umbraco.Core.Persistence.Repositories
return s;
};
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
return ProcessQuery(translate(sqlBaseFull), translate(sqlBaseIds));
return ProcessQuery(translate(sqlBaseFull), new PagingSqlQuery(translate(sqlBaseIds)));
}
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
{
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
Func<SqlTranslator<IContent>, Sql> translate = (translator) =>
@@ -103,13 +104,25 @@ namespace Umbraco.Core.Persistence.Repositories
var translatorFull = new SqlTranslator<IContent>(sqlBaseFull, query);
var translatorIds = new SqlTranslator<IContent>(sqlBaseIds, query);
return ProcessQuery(translate(translatorFull), translate(translatorIds));
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)));
}
#endregion
#region Overrides of PetaPocoRepositoryBase<IContent>
/// <summary>
/// Returns the base query to return Content
/// </summary>
/// <param name="queryType"></param>
/// <returns></returns>
/// <remarks>
/// Content queries will differ depending on what needs to be returned:
/// * FullSingle: When querying for a single document, this will include the Outer join to fetch the content item's published version info
/// * FullMultiple: When querying for multiple documents, this will exclude the Outer join to fetch the content item's published version info - this info would need to be fetched separately
/// * Ids: This would essentially be the same as FullMultiple however the columns specified will only return the Ids for the documents
/// * Count: A query to return the count for documents
/// </remarks>
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
@@ -122,14 +135,14 @@ namespace Umbraco.Core.Persistence.Repositories
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
if (queryType == BaseQueryType.Full)
if (queryType == BaseQueryType.FullSingle)
{
//The only reason we apply this left outer join is to be able to pull back the DocumentPublishedReadOnlyDto
//information with the entire data set, so basically this will get both the latest document and also it's published
//version if it has one. When performing a count or when just retrieving Ids like in paging, this is unecessary
//version if it has one. When performing a count or when retrieving Ids like in paging, this is unecessary
//and causes huge performance overhead for the SQL server, especially when sorting the result.
//To fix this perf overhead we'd need another index on :
// CREATE NON CLUSTERED INDEX ON cmsDocument.node + cmsDocument.published
//We also don't include this outer join when querying for multiple entities since it is much faster to fetch this information
//in a separate query. For a single entity this is ok.
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
SqlSyntax.GetQuotedTableName("cmsDocument"),
@@ -151,7 +164,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
@@ -222,10 +235,10 @@ namespace Umbraco.Core.Persistence.Repositories
while (true)
{
// get the next group of nodes
var sqlFull = translate(baseId, GetBaseQuery(BaseQueryType.Full));
var sqlFull = translate(baseId, GetBaseQuery(BaseQueryType.FullMultiple));
var sqlIds = translate(baseId, GetBaseQuery(BaseQueryType.Ids));
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(sqlFull, groupSize), SqlSyntax.SelectTop(sqlIds, groupSize))
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(sqlFull, groupSize), new PagingSqlQuery(SqlSyntax.SelectTop(sqlIds, groupSize)))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -245,7 +258,7 @@ namespace Umbraco.Core.Persistence.Repositories
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
}
}
baseId = xmlItems.Last().NodeId;
baseId = xmlItems[xmlItems.Count - 1].NodeId;
}
}
@@ -257,15 +270,15 @@ namespace Umbraco.Core.Persistence.Repositories
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
};
var sqlFull = translate(GetBaseQuery(BaseQueryType.Full));
var sqlFull = translate(GetBaseQuery(BaseQueryType.FullMultiple));
var sqlIds = translate(GetBaseQuery(BaseQueryType.Ids));
return ProcessQuery(sqlFull, sqlIds, true);
return ProcessQuery(sqlFull, new PagingSqlQuery(sqlIds), true);
}
public override IContent GetByVersion(Guid versionId)
{
var sql = GetBaseQuery(false);
var sql = GetBaseQuery(BaseQueryType.FullSingle);
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
@@ -675,12 +688,12 @@ namespace Umbraco.Core.Persistence.Repositories
// ORDER BY substring(path, 1, len(path) - charindex(',', reverse(path))), sortOrder
// but that's probably an overkill - sorting by level,sortOrder should be enough
var sqlFull = GetBaseQuery(BaseQueryType.Full);
var sqlFull = GetBaseQuery(BaseQueryType.FullMultiple);
var translatorFull = new SqlTranslator<IContent>(sqlFull, query);
var sqlIds = GetBaseQuery(BaseQueryType.Ids);
var translatorIds = new SqlTranslator<IContent>(sqlIds, query);
return ProcessQuery(translate(translatorFull), translate(translatorIds), true);
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)), true);
}
/// <summary>
@@ -772,13 +785,8 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
public void ClearPublished(IContent content)
{
// race cond!
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@id AND published=@published", new { id = content.Id, published = true });
foreach (var documentDto in documentDtos)
{
documentDto.Published = false;
Database.Update(documentDto);
}
var sql = "UPDATE cmsDocument SET published=0 WHERE nodeId=@id AND published=1";
Database.Execute(sql, new {id = content.Id});
}
/// <summary>
@@ -860,7 +868,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
return GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsDocument", "nodeId"),
(sqlFull, sqlIds) => ProcessQuery(sqlFull, sqlIds), orderBy, orderDirection, orderBySystemField,
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -890,23 +898,58 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
return base.GetDatabaseFieldNameForOrderBy(orderBy);
}
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL with the outer join to return all data required to create an IContent
/// The FullMultiple SQL without the outer join to return all data required to create an IContent excluding it's published state data which this will query separately
/// </param>
/// <param name="sqlIds">
/// <param name="pagingSqlQuery">
/// The Id SQL without the outer join to just return all document ids - used to process the properties for the content item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IContent> ProcessQuery(Sql sqlFull, Sql sqlIds, bool withCache = false)
private IEnumerable<IContent> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sqlFull);
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
//Go and get all of the published version data separately for this data, this is because when we are querying
//for multiple content items we don't include the outer join to fetch this data in the same query because
//it is insanely slow. Instead we just fetch the published version data separately in one query.
//we need to parse the original SQL statement and reduce the columns to just cmsDocument.nodeId so that we can use
// the statement to go get the published data for all of the items by using an inner join
var parsedOriginalSql = "SELECT cmsDocument.nodeId " + sqlFull.SQL.Substring(sqlFull.SQL.IndexOf("FROM", StringComparison.Ordinal));
//now remove everything from an Orderby clause and beyond
if (parsedOriginalSql.InvariantContains("ORDER BY "))
{
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
}
var publishedSql = new Sql(@"SELECT *
FROM cmsDocument AS doc2
INNER JOIN
(" + parsedOriginalSql + @") as docData
ON doc2.nodeId = docData.nodeId
WHERE doc2.published = 1
ORDER BY doc2.nodeId
", sqlFull.Arguments);
//go and get the published version data, we do a Query here and not a Fetch so we are
//not allocating a whole list to memory just to allocate another list in memory since
//we are assigning this data to a keyed collection for fast lookup below
var publishedData = Database.Query<DocumentPublishedReadOnlyDto>(publishedSql);
var publishedDataCollection = new DocumentPublishedReadOnlyDtoCollection();
foreach (var publishedDto in publishedData)
{
//double check that there's no corrupt db data, there should only be a single published item
if (publishedDataCollection.Contains(publishedDto.NodeId) == false)
publishedDataCollection.Add(publishedDto);
}
var content = new IContent[dtos.Count];
var defs = new List<DocumentDefinition>();
@@ -920,6 +963,8 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
for (var i = 0; i < dtos.Count; i++)
{
var dto = dtos[i];
DocumentPublishedReadOnlyDto publishedDto;
publishedDataCollection.TryGetValue(dto.NodeId, out publishedDto);
// if the cache contains the published version, use it
if (withCache)
@@ -947,7 +992,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId] = contentType;
}
content[i] = ContentFactory.BuildEntity(dto, contentType);
content[i] = ContentFactory.BuildEntity(dto, contentType, publishedDto);
// need template
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
@@ -968,7 +1013,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
.ToDictionary(x => x.Id, x => x);
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(sqlIds, defs);
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
// assign
var dtoIndex = 0;
@@ -1056,7 +1101,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
return currentName;
}
/// <summary>
/// Dispose disposable properties
/// </summary>
@@ -1071,5 +1116,26 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
_contentPreviewRepository.Dispose();
_contentXmlRepository.Dispose();
}
/// <summary>
/// A keyed collection for fast lookup when retrieving a separate list of published version data
/// </summary>
private class DocumentPublishedReadOnlyDtoCollection : KeyedCollection<int, DocumentPublishedReadOnlyDto>
{
protected override int GetKeyForItem(DocumentPublishedReadOnlyDto item)
{
return item.NodeId;
}
public bool TryGetValue(int key, out DocumentPublishedReadOnlyDto val)
{
if (Dictionary == null)
{
val = null;
return false;
}
return Dictionary.TryGetValue(key, out val);
}
}
}
}
@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Persistence.Repositories
{
// cannot kill in v7 because it is public, kill in v8
[Obsolete("Use MediaFileSystem.DeleteMediaFiles instead.", false)]
public interface IDeleteMediaFilesRepository
{
/// <summary>
@@ -9,6 +12,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="files"></param>
/// <returns></returns>
[Obsolete("Use MediaFileSystem.DeleteMediaFiles instead.", false)]
bool DeleteMediaFiles(IEnumerable<string> files);
}
}
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
}
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query)
@@ -78,7 +78,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
#endregion
@@ -100,7 +100,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
@@ -143,13 +143,24 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, true);
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
}
private IEnumerable<IMedia> ProcessQuery(Sql sql, bool withCache = false)
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all media data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all media ids - used to process the properties for the media item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IMedia> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sqlFull);
var content = new IMedia[dtos.Count];
var defs = new List<DocumentDefinition>();
@@ -200,7 +211,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(sql, defs);
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
// assign
var dtoIndex = 0;
@@ -257,7 +268,8 @@ namespace Umbraco.Core.Persistence.Repositories
query = query
.Where<NodeDto>(x => x.NodeId > baseId, SqlSyntax)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
var sql = SqlSyntax.SelectTop(query, groupSize);
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -505,7 +517,7 @@ namespace Umbraco.Core.Persistence.Repositories
return GetPagedResultsByQuery<ContentVersionDto>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsContentVersion", "contentId"),
(sqlFull, sqlIds) => ProcessQuery(sqlFull), orderBy, orderDirection, orderBySystemField,
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -513,7 +525,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Private method to create a media object from a ContentDto
/// </summary>
/// <param name="d"></param>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
@@ -525,7 +537,7 @@ namespace Umbraco.Core.Persistence.Repositories
var docDef = new DocumentDefinition(dto.NodeId, versionId, media.UpdateDate, media.CreateDate, contentType);
var properties = GetPropertyCollection(docSql, new[] { docDef });
var properties = GetPropertyCollection(new PagingSqlQuery(docSql), new[] { docDef });
media.Properties = properties[dto.NodeId];
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
}
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
@@ -90,7 +90,7 @@ namespace Umbraco.Core.Persistence.Repositories
baseQuery.Append(new Sql("WHERE umbracoNode.id IN (" + sql.SQL + ")", sql.Arguments))
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(baseQuery);
return ProcessQuery(baseQuery, new PagingSqlQuery(baseQuery));
}
else
{
@@ -98,7 +98,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
}
@@ -129,7 +129,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
@@ -385,7 +385,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, true);
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
}
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
@@ -408,7 +408,8 @@ namespace Umbraco.Core.Persistence.Repositories
query = query
.Where<NodeDto>(x => x.NodeId > baseId)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
var sql = SqlSyntax.SelectTop(query, groupSize);
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -449,7 +450,7 @@ namespace Umbraco.Core.Persistence.Repositories
var factory = new MemberFactory(memberType, NodeObjectTypeId, dto.NodeId);
var media = factory.BuildEntity(dto);
var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.ContentVersionDto.VersionId, media.UpdateDate, media.CreateDate, memberType) });
var properties = GetPropertyCollection(new PagingSqlQuery(sql), new[] { new DocumentDefinition(dto.NodeId, dto.ContentVersionDto.VersionId, media.UpdateDate, media.CreateDate, memberType) });
media.Properties = properties[dto.NodeId];
@@ -540,7 +541,7 @@ namespace Umbraco.Core.Persistence.Repositories
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
@@ -601,7 +602,7 @@ namespace Umbraco.Core.Persistence.Repositories
return GetPagedResultsByQuery<MemberDto>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsMember", "nodeId"),
(sqlFull, sqlIds) => ProcessQuery(sqlFull), orderBy, orderDirection, orderBySystemField,
(sqlFull, sqlIds) => ProcessQuery(sqlFull, sqlIds), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -641,10 +642,21 @@ namespace Umbraco.Core.Persistence.Repositories
return base.GetEntityPropertyNameForOrderBy(orderBy);
}
private IEnumerable<IMember> ProcessQuery(Sql sql, bool withCache = false)
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all member data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all member ids - used to process the properties for the member item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IMember> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql);
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
var content = new IMember[dtos.Count];
var defs = new List<DocumentDefinition>();
@@ -681,7 +693,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(sql, defs);
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
// assign
var dtoIndex = 0;
@@ -63,6 +63,7 @@ namespace Umbraco.Core.Persistence.Repositories
FormatDeleteStatement("cmsContentVersion", "ContentId"),
FormatDeleteStatement("cmsContentXml", "nodeId"),
FormatDeleteStatement("cmsContent", "nodeId"),
//TODO: Why is this being done? We just delete this exact data in the next line
"UPDATE umbracoNode SET parentID = '" + RecycleBinId + "' WHERE trashed = '1' AND nodeObjectType = @NodeObjectType",
"DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType"
};
@@ -91,14 +92,18 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
/// <summary>
/// A delete statement that will delete anything in the table specified where its PK (keyName) is found in the
/// list of umbracoNode.id that have trashed flag set
/// </summary>
/// <param name="tableName"></param>
/// <param name="keyName"></param>
/// <returns></returns>
private string FormatDeleteStatement(string tableName, string keyName)
{
//This query works with sql ce and sql server:
//DELETE FROM umbracoUser2NodeNotify WHERE umbracoUser2NodeNotify.nodeId IN
//(SELECT nodeId FROM umbracoUser2NodeNotify as TB1 INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972')
return
string.Format(
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT TB1.{1} FROM {0} as TB1 INNER JOIN umbracoNode as TB2 ON TB1.{1} = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT id FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType)",
tableName, keyName);
}
@@ -432,7 +432,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <exception cref="System.ArgumentNullException">orderBy</exception>
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
Tuple<string, string> nodeIdSelect,
Func<Sql, Sql, IEnumerable<TEntity>> processQuery,
Func<Sql, PagingSqlQuery<TDto>, IEnumerable<TEntity>> processQuery,
string orderBy,
Direction orderDirection,
bool orderBySystemField,
@@ -443,7 +443,7 @@ namespace Umbraco.Core.Persistence.Repositories
// Get base query for returning IDs
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
// Get base query for returning all data
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
if (query == null) query = new Query<TEntity>();
var translatorIds = new SqlTranslator<TEntity>(sqlBaseIds, query);
@@ -466,7 +466,7 @@ namespace Umbraco.Core.Persistence.Repositories
// the pageResult, then the GetAll will actually return ALL records in the db.
if (pagedResult.Items.Any())
{
//Crete the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
var args = sqlNodeIdsWithSort.Arguments;
string sqlStringCount, sqlStringPage;
Database.BuildPageQueries<TDto>(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage);
@@ -486,8 +486,8 @@ namespace Umbraco.Core.Persistence.Repositories
var fullQuery = GetSortedSqlForPagedResults(
GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, defaultFilter),
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
return processQuery(fullQuery, sqlNodeIdsWithSort);
return processQuery(fullQuery, new PagingSqlQuery<TDto>(Database, sqlNodeIdsWithSort, pageIndex, pageSize));
}
else
{
@@ -497,18 +497,47 @@ namespace Umbraco.Core.Persistence.Repositories
return result;
}
/// <summary>
/// Gets the property collection for a non-paged query
/// </summary>
/// <param name="sql"></param>
/// <param name="documentDefs"></param>
/// <returns></returns>
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
Sql docSql,
Sql sql,
IReadOnlyCollection<DocumentDefinition> documentDefs)
{
return GetPropertyCollection(new PagingSqlQuery(sql), documentDefs);
}
/// <summary>
/// Gets the property collection for a query
/// </summary>
/// <param name="pagingSqlQuery"></param>
/// <param name="documentDefs"></param>
/// <returns></returns>
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
PagingSqlQuery pagingSqlQuery,
IReadOnlyCollection<DocumentDefinition> documentDefs)
{
if (documentDefs.Count == 0) return new Dictionary<int, PropertyCollection>();
//initialize to the query passed in
var docSql = pagingSqlQuery.PrePagedSql;
//we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use
// the statement to go get the property data for all of the items by using an inner join
var parsedOriginalSql = "SELECT {0} " + docSql.SQL.Substring(docSql.SQL.IndexOf("FROM", StringComparison.Ordinal));
//now remove everything from an Orderby clause and beyond
if (parsedOriginalSql.InvariantContains("ORDER BY "))
if (pagingSqlQuery.HasPaging)
{
//if this is a paged query, build the paged query with the custom column substitution, then re-assign
docSql = pagingSqlQuery.BuildPagedQuery("{0}");
parsedOriginalSql = docSql.SQL;
}
else if (parsedOriginalSql.InvariantContains("ORDER BY "))
{
//now remove everything from an Orderby clause and beyond if this is unpaged data
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
}
@@ -525,7 +554,7 @@ WHERE EXISTS(
INNER JOIN cmsPropertyType
ON b.datatypeNodeId = cmsPropertyType.dataTypeId
INNER JOIN
(" + string.Format(parsedOriginalSql, "DISTINCT cmsContent.contentType") + @") as docData
(" + string.Format(parsedOriginalSql, "cmsContent.contentType") + @") as docData
ON cmsPropertyType.contentTypeId = docData.contentType
WHERE a.id = b.id)", docSql.Arguments);
@@ -646,28 +675,7 @@ ORDER BY contentNodeId, propertytypeid
return result;
}
public class DocumentDefinition
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public DocumentDefinition(int id, Guid version, DateTime versionDate, DateTime createDate, IContentTypeComposition composition)
{
Id = id;
Version = version;
VersionDate = versionDate;
CreateDate = createDate;
Composition = composition;
}
public int Id { get; set; }
public Guid Version { get; set; }
public DateTime VersionDate { get; set; }
public DateTime CreateDate { get; set; }
public IContentTypeComposition Composition { get; set; }
}
protected virtual string GetDatabaseFieldNameForOrderBy(string orderBy)
{
// Translate the passed order by field (which were originally defined for in-memory object sorting
@@ -763,5 +771,92 @@ ORDER BY contentNodeId, propertytypeid
/// <param name="queryType"></param>
/// <returns></returns>
protected abstract Sql GetBaseQuery(BaseQueryType queryType);
internal class DocumentDefinition
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public DocumentDefinition(int id, Guid version, DateTime versionDate, DateTime createDate, IContentTypeComposition composition)
{
Id = id;
Version = version;
VersionDate = versionDate;
CreateDate = createDate;
Composition = composition;
}
public int Id { get; set; }
public Guid Version { get; set; }
public DateTime VersionDate { get; set; }
public DateTime CreateDate { get; set; }
public IContentTypeComposition Composition { get; set; }
}
/// <summary>
/// An object representing a query that may contain paging information
/// </summary>
internal class PagingSqlQuery
{
public Sql PrePagedSql { get; private set; }
public PagingSqlQuery(Sql prePagedSql)
{
PrePagedSql = prePagedSql;
}
public virtual bool HasPaging
{
get { return false; }
}
public virtual Sql BuildPagedQuery(string selectColumns)
{
throw new InvalidOperationException("This query has no paging information");
}
}
/// <summary>
/// An object representing a query that contains paging information
/// </summary>
/// <typeparam name="T"></typeparam>
internal class PagingSqlQuery<T> : PagingSqlQuery
{
private readonly Database _db;
private readonly long _pageIndex;
private readonly int _pageSize;
public PagingSqlQuery(Database db, Sql prePagedSql, long pageIndex, int pageSize) : base(prePagedSql)
{
_db = db;
_pageIndex = pageIndex;
_pageSize = pageSize;
}
public override bool HasPaging
{
get { return _pageSize > 0; }
}
/// <summary>
/// Creates a paged query based on the original query and subtitutes the selectColumns specified
/// </summary>
/// <param name="selectColumns"></param>
/// <returns></returns>
public override Sql BuildPagedQuery(string selectColumns)
{
if (HasPaging == false) throw new InvalidOperationException("This query has no paging information");
var resultSql = string.Format("SELECT {0} {1}", selectColumns, PrePagedSql.SQL.Substring(PrePagedSql.SQL.IndexOf("FROM", StringComparison.Ordinal)));
//this query is meant to be paged so we need to generate the paging syntax
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
var args = PrePagedSql.Arguments;
string sqlStringCount, sqlStringPage;
_db.BuildPageQueries<T>(_pageIndex * _pageSize, _pageSize, resultSql, ref args, out sqlStringCount, out sqlStringPage);
return new Sql(sqlStringPage, args);
}
}
}
}
@@ -1,7 +1,23 @@
namespace Umbraco.Core.Persistence.SqlSyntax
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Persistence.SqlSyntax
{
internal static class SqlSyntaxProviderExtensions
{
public static IEnumerable<DbIndexDefinition> GetDefinedIndexesDefinitions(this ISqlSyntaxProvider sql, Database db)
{
return sql.GetDefinedIndexes(db)
.Select(x => new DbIndexDefinition()
{
TableName = x.Item1,
IndexName = x.Item2,
ColumnName = x.Item3,
IsUnique = x.Item4
}).ToArray();
}
/// <summary>
/// Returns the quotes tableName.columnName combo
/// </summary>
@@ -15,6 +15,7 @@ namespace Umbraco.Core.PropertyEditors
public PreValueField()
{
Validators = new List<IPropertyValidator>();
Config = new Dictionary<string, object>();
//check for an attribute and fill the values
var att = GetType().GetCustomAttribute<PreValueFieldAttribute>(false);
@@ -79,5 +80,11 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
[JsonProperty("validation", ItemConverterType = typeof(ManifestValidatorConverter))]
public List<IPropertyValidator> Validators { get; private set; }
/// <summary>
/// This allows for custom configuration to be injected into the pre-value editor
/// </summary>
[JsonProperty("config")]
public IDictionary<string, object> Config { get; set; }
}
}
@@ -38,6 +38,7 @@ namespace Umbraco.Core.PropertyEditors
IsParameterEditor = _attribute.IsParameterEditor;
Icon = _attribute.Icon;
Group = _attribute.Group;
IsDeprecated = _attribute.IsDeprecated;
}
}
@@ -90,6 +91,9 @@ namespace Umbraco.Core.PropertyEditors
get { return CreateValueEditor(); }
}
[JsonIgnore]
public bool IsDeprecated { get; internal set; }
[JsonIgnore]
IValueEditor IParameterEditor.ValueEditor
{
@@ -60,6 +60,12 @@ namespace Umbraco.Core.PropertyEditors
public string ValueType { get; set; }
public bool IsParameterEditor { get; set; }
/// <summary>
/// If set to true, this property editor will not show up in the DataType's drop down list
/// if there is not already one of them chosen for a DataType
/// </summary>
public bool IsDeprecated { get; set; }
/// <summary>
/// If this is is true than the editor will be displayed full width without a label
/// </summary>
-13
View File
@@ -1,13 +0,0 @@
using System;
namespace Umbraco.Core.Scoping
{
[Flags]
public enum ActionTime
{
None = 0,
BeforeCommit = 1,
BeforeEvents = 2,
BeforeDispose = 4
}
}
@@ -0,0 +1,9 @@
using System;
namespace Umbraco.Core.Scoping
{
public interface IInstanceIdentifiable
{
Guid InstanceId { get; }
}
}
+4 -6
View File
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Scoping
/// <summary>
/// Represents a scope.
/// </summary>
public interface IScope : IDisposable
public interface IScope : IDisposable, IInstanceIdentifiable
{
/// <summary>
/// Gets the scope database.
@@ -38,10 +38,8 @@ namespace Umbraco.Core.Scoping
/// <summary>
/// Completes the scope.
/// </summary>
void Complete();
#if DEBUG_SCOPES
Guid InstanceId { get; }
#endif
/// <returns>A value indicating whether the scope has been successfully completed.</returns>
/// <remarks>Can return false if any child scope has not completed.</remarks>
bool Complete();
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Scoping
internal interface IScopeInternal : IScope
{
IScopeInternal ParentScope { get; }
EventsDispatchMode DispatchMode { get; }
bool CallContext { get; }
IsolationLevel IsolationLevel { get; }
UmbracoDatabase DatabaseOrNull { get; }
EventMessages MessagesOrNull { get; }
+10 -5
View File
@@ -1,4 +1,5 @@
using System.Data;
using System;
using System.Data;
using Umbraco.Core.Events;
#if DEBUG_SCOPES
using System.Collections.Generic;
@@ -23,8 +24,9 @@ namespace Umbraco.Core.Scoping
IScope CreateScope(
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
bool? scopeFileSystems = null);
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false);
/// <summary>
/// Creates a detached scope.
@@ -37,17 +39,18 @@ namespace Umbraco.Core.Scoping
IScope CreateDetachedScope(
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null);
/// <summary>
/// Attaches a scope.
/// </summary>
/// <param name="scope">The scope to attach.</param>
/// <param name="callContext">A value indicating whether to force usage of call context.</param>
/// <remarks>
/// <para>Only a scope created by <see cref="CreateDetachedScope"/> can be attached.</para>
/// </remarks>
void AttachScope(IScope scope);
void AttachScope(IScope scope, bool callContext = false);
/// <summary>
/// Detaches a scope.
@@ -64,7 +67,9 @@ namespace Umbraco.Core.Scoping
ScopeContext Context { get; }
#if DEBUG_SCOPES
Dictionary<Guid, object> CallContextObjects { get; }
IEnumerable<ScopeInfo> ScopeInfos { get; }
ScopeInfo GetScopeInfo(IScope scope);
#endif
}
}
+5 -6
View File
@@ -24,10 +24,11 @@ namespace Umbraco.Core.Scoping
#endif
}
#if DEBUG_SCOPES
private readonly Guid _instanceId = Guid.NewGuid();
public Guid InstanceId { get { return _instanceId; } }
#endif
/// <inheritdoc />
public bool CallContext { get { return false; } }
/// <inheritdoc />
public RepositoryCacheMode RepositoryCacheMode
@@ -75,7 +76,7 @@ namespace Umbraco.Core.Scoping
}
/// <inheritdoc />
public void Complete()
public bool Complete()
{
throw new NotSupportedException();
}
@@ -100,15 +101,13 @@ namespace Umbraco.Core.Scoping
if (_database != null)
_database.Dispose();
_scopeProvider.AmbientScope = null;
_scopeProvider.AmbientContext = null;
_scopeProvider.SetAmbient(null);
_disposed = true;
GC.SuppressFinalize(this);
}
public IScopeInternal ParentScope { get { return null; } }
public EventsDispatchMode DispatchMode { get {return EventsDispatchMode.Unspecified; } }
public IsolationLevel IsolationLevel { get {return IsolationLevel.Unspecified; } }
public bool ScopedFileSystems { get { return false; } }
public void ChildCompleted(bool? completed) { }
+56 -37
View File
@@ -16,9 +16,9 @@ namespace Umbraco.Core.Scoping
private readonly ScopeProvider _scopeProvider;
private readonly IsolationLevel _isolationLevel;
private readonly RepositoryCacheMode _repositoryCacheMode;
private readonly EventsDispatchMode _dispatchMode;
private readonly bool? _scopeFileSystem;
private readonly ScopeContext _scopeContext;
private bool _callContext;
private bool _disposed;
private bool? _completed;
@@ -35,15 +35,17 @@ namespace Umbraco.Core.Scoping
Scope parent, ScopeContext scopeContext, bool detachable,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
bool? scopeFileSystems = null)
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false)
{
_scopeProvider = scopeProvider;
_scopeContext = scopeContext;
_isolationLevel = isolationLevel;
_repositoryCacheMode = repositoryCacheMode;
_dispatchMode = dispatchMode;
_eventDispatcher = eventDispatcher;
_scopeFileSystem = scopeFileSystems;
_callContext = callContext;
Detachable = detachable;
#if DEBUG_SCOPES
@@ -74,9 +76,9 @@ namespace Umbraco.Core.Scoping
if (repositoryCacheMode != RepositoryCacheMode.Unspecified && parent.RepositoryCacheMode != repositoryCacheMode)
throw new ArgumentException("Cannot be different from parent.", "repositoryCacheMode");
// cannot specify a different mode!
if (_dispatchMode != EventsDispatchMode.Unspecified && parent._dispatchMode != dispatchMode)
throw new ArgumentException("Cannot be different from parent.", "dispatchMode");
// cannot specify a dispatcher!
if (_eventDispatcher != null)
throw new ArgumentException("Cannot be specified on nested scope.", "eventDispatcher");
// cannot specify a different fs scope!
if (scopeFileSystems != null && parent._scopeFileSystem != scopeFileSystems)
@@ -97,30 +99,31 @@ namespace Umbraco.Core.Scoping
ScopeContext scopeContext,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
bool? scopeFileSystems = null)
: this(scopeProvider, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems)
{
}
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false)
: this(scopeProvider, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext)
{ }
// initializes a new scope in a nested scopes chain, with its parent
public Scope(ScopeProvider scopeProvider, Scope parent,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
bool? scopeFileSystems = null)
: this(scopeProvider, parent, null, false, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems)
{
}
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false)
: this(scopeProvider, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext)
{ }
// initializes a new scope, replacing a NoScope instance
public Scope(ScopeProvider scopeProvider, NoScope noScope,
ScopeContext scopeContext,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
bool? scopeFileSystems = null)
: this(scopeProvider, null, scopeContext, false, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems)
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false)
: this(scopeProvider, null, scopeContext, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext)
{
// steal everything from NoScope
_database = noScope.DatabaseOrNull;
@@ -130,10 +133,20 @@ namespace Umbraco.Core.Scoping
throw new Exception("NoScope instance is not free.");
}
#if DEBUG_SCOPES
private readonly Guid _instanceId = Guid.NewGuid();
public Guid InstanceId { get { return _instanceId; } }
#endif
// a value indicating whether to force call-context
public bool CallContext
{
get
{
if (_callContext) return true;
if (ParentScope != null) return ParentScope.CallContext;
return false;
}
set { _callContext = value; }
}
public bool ScopedFileSystems
{
@@ -144,16 +157,6 @@ namespace Umbraco.Core.Scoping
}
}
public EventsDispatchMode DispatchMode
{
get
{
if (_dispatchMode != EventsDispatchMode.Unspecified) return _dispatchMode;
if (ParentScope != null) return ParentScope.DispatchMode;
return EventsDispatchMode.Scope;
}
}
/// <inheritdoc />
public RepositoryCacheMode RepositoryCacheMode
{
@@ -184,6 +187,8 @@ namespace Umbraco.Core.Scoping
// the parent scope (in a nested scopes chain)
public IScopeInternal ParentScope { get; set; }
public bool Attached { get; set; }
// the original scope (when attaching a detachable scope)
public IScopeInternal OrigScope { get; set; }
@@ -290,15 +295,16 @@ namespace Umbraco.Core.Scoping
{
EnsureNotDisposed();
if (ParentScope != null) return ParentScope.Events;
return _eventDispatcher ?? (_eventDispatcher = new ScopeEventDispatcher(DispatchMode));
return _eventDispatcher ?? (_eventDispatcher = new ScopeEventDispatcher());
}
}
/// <inheritdoc />
public void Complete()
public bool Complete()
{
if (_completed.HasValue == false)
_completed = true;
return _completed.Value;
}
public void Reset()
@@ -324,7 +330,18 @@ namespace Umbraco.Core.Scoping
EnsureNotDisposed();
if (this != _scopeProvider.AmbientScope)
{
#if DEBUG_SCOPES
var ambient = _scopeProvider.AmbientScope;
Logging.LogHelper.Debug<Scope>("Dispose error (" + (ambient == null ? "no" : "other") + " ambient)");
if (ambient == null)
throw new InvalidOperationException("Not the ambient scope (no ambient scope).");
var infos = _scopeProvider.GetScopeInfo(ambient);
throw new InvalidOperationException("Not the ambient scope (see current ambient ctor stack trace).\r\n" + infos.CtorStack);
#else
throw new InvalidOperationException("Not the ambient scope.");
#endif
}
#if DEBUG_SCOPES
_scopeProvider.Disposed(this);
@@ -418,7 +435,7 @@ namespace Umbraco.Core.Scoping
}
finally
{
_scopeProvider.AmbientContext = null;
_scopeProvider.SetAmbient(null);
}
}
}, () =>
@@ -426,8 +443,10 @@ namespace Umbraco.Core.Scoping
if (Detachable)
{
// get out of the way, restore original
_scopeProvider.AmbientScope = OrigScope;
_scopeProvider.AmbientContext = OrigContext;
_scopeProvider.SetAmbient(OrigScope, OrigContext);
Attached = false;
OrigScope = null;
OrigContext = null;
}
});
}
+4 -1
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Umbraco.Core.Scoping
{
public class ScopeContext
public class ScopeContext : IInstanceIdentifiable
{
private Dictionary<string, IEnlistedObject> _enlisted;
@@ -27,6 +27,9 @@ namespace Umbraco.Core.Scoping
throw new AggregateException("Exceptions were thrown by listed actions.", exceptions);
}
private readonly Guid _instanceId = Guid.NewGuid();
public Guid InstanceId { get { return _instanceId; } }
private IDictionary<string, IEnlistedObject> Enlisted
{
get
+291 -110
View File
@@ -1,11 +1,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Web;
using Umbraco.Core.Events;
using Umbraco.Core.Persistence;
#if DEBUG_SCOPES
using System.Linq;
#endif
namespace Umbraco.Core.Scoping
{
@@ -24,17 +27,17 @@ namespace Umbraco.Core.Scoping
SafeCallContext.Register(
() =>
{
var scope = StaticAmbientScope;
var context = StaticAmbientContext;
StaticAmbientScope = null;
StaticAmbientContext = null;
var scope = GetCallContextObject<IScopeInternal>(ScopeItemKey);
var context = GetCallContextObject<ScopeContext>(ContextItemKey);
SetCallContextObject(ScopeItemKey, null);
SetCallContextObject(ContextItemKey, null);
return Tuple.Create(scope, context);
},
o =>
{
// cannot re-attached over leaked scope/context
// except of course over NoScope (which leaks)
var ambientScope = StaticAmbientScope;
var ambientScope = GetCallContextObject<IScope>(ScopeItemKey);
if (ambientScope != null)
{
var ambientNoScope = ambientScope as NoScope;
@@ -44,59 +47,210 @@ namespace Umbraco.Core.Scoping
// this should rollback any pending transaction
ambientNoScope.Dispose();
}
if (StaticAmbientContext != null) throw new Exception("Found leaked context when restoring call context.");
if (GetCallContextObject<ScopeContext>(ContextItemKey) != null)
throw new Exception("Found leaked context when restoring call context.");
var t = (Tuple<IScopeInternal, ScopeContext>)o;
StaticAmbientScope = t.Item1;
StaticAmbientContext = t.Item2;
var t = (Tuple<IScopeInternal, ScopeContext>) o;
SetCallContextObject(ScopeItemKey, t.Item1);
SetCallContextObject(ContextItemKey, t.Item2);
});
}
public IDatabaseFactory2 DatabaseFactory { get; private set; }
#region Context
// objects that go into the logical call context better be serializable else they'll eventually
// cause issues whenever some cross-AppDomain code executes - could be due to ReSharper running
// tests, any other things (see https://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx),
// but we don't want to make all of our objects serializable since they are *not* meant to be
// used in cross-AppDomain scenario anyways.
// in addition, whatever goes into the logical call context is serialized back and forth any
// time cross-AppDomain code executes, so if we put an "object" there, we'll can *another*
// "object" instance - and so we cannot use a random object as a key.
// so what we do is: we register a guid in the call context, and we keep a table mapping those
// guids to the actual objects. the guid serializes back and forth without causing any issue,
// and we can retrieve the actual objects from the table.
// only issue: how are we supposed to clear the table? we can't, really. objects should take
// care of de-registering themselves from context.
// everything we use does, except the NoScope scope, which just stays there
//
// during tests, NoScope can to into call context... nothing much we can do about it
private static readonly object StaticCallContextObjectsLock = new object();
private static readonly Dictionary<Guid, object> StaticCallContextObjects
= new Dictionary<Guid, object>();
#if DEBUG_SCOPES
public Dictionary<Guid, object> CallContextObjects
{
get
{
lock (StaticCallContextObjectsLock)
{
// capture in a dictionary
return StaticCallContextObjects.ToDictionary(x => x.Key, x => x.Value);
}
}
}
#endif
private static T GetCallContextObject<T>(string key)
where T : class
{
var objectKey = CallContext.LogicalGetData(key).AsGuid();
lock (StaticCallContextObjectsLock)
{
object callContextObject;
if (StaticCallContextObjects.TryGetValue(objectKey, out callContextObject))
{
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("GotObject " + objectKey.ToString("N").Substring(0, 8));
#endif
return (T) callContextObject;
}
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("MissedObject " + objectKey.ToString("N").Substring(0, 8));
#endif
return null;
}
}
private static void SetCallContextObject(string key, IInstanceIdentifiable value)
{
#if DEBUG_SCOPES
// manage the 'context' that contains the scope (null, "http" or "call")
// only for scopes of course!
if (key == ScopeItemKey)
{
// first, null-register the existing value
var ambientKey = CallContext.LogicalGetData(ScopeItemKey).AsGuid();
object o = null;
lock (StaticCallContextObjectsLock)
{
if (ambientKey != default(Guid))
StaticCallContextObjects.TryGetValue(ambientKey, out o);
}
var ambientScope = o as IScope;
if (ambientScope != null) RegisterContext(ambientScope, null);
// then register the new value
var scope = value as IScope;
if (scope != null) RegisterContext(scope, "call");
}
#endif
if (value == null)
{
var objectKey = CallContext.LogicalGetData(key).AsGuid();
CallContext.FreeNamedDataSlot(key);
if (objectKey == default (Guid)) return;
lock (StaticCallContextObjectsLock)
{
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("RemoveObject " + objectKey.ToString("N").Substring(0, 8));
#endif
StaticCallContextObjects.Remove(objectKey);
}
}
else
{
// note - we are *not* detecting an already-existing value
// because our code in this class *always* sets to null before
// setting to a real value
var objectKey = value.InstanceId;
lock (StaticCallContextObjectsLock)
{
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("AddObject " + objectKey.ToString("N").Substring(0, 8));
#endif
StaticCallContextObjects.Add(objectKey, value);
}
CallContext.LogicalSetData(key, objectKey);
}
}
internal static Func<IDictionary> HttpContextItemsGetter { get; set; }
private static IDictionary HttpContextItems
{
get
{
return HttpContextItemsGetter == null
? (HttpContext.Current == null ? null : HttpContext.Current.Items)
: HttpContextItemsGetter();
}
}
public static T GetHttpContextObject<T>(string key, bool required = true)
where T : class
{
var httpContextItems = HttpContextItems;
if (httpContextItems != null)
return (T)httpContextItems[key];
if (required)
throw new Exception("HttpContext.Current is null.");
return null;
}
private static bool SetHttpContextObject(string key, object value, bool required = true)
{
var httpContextItems = HttpContextItems;
if (httpContextItems == null)
{
if (required)
throw new Exception("HttpContext.Current is null.");
return false;
}
#if DEBUG_SCOPES
// manage the 'context' that contains the scope (null, "http" or "call")
// only for scopes of course!
if (key == ScopeItemKey)
{
// first, null-register the existing value
var ambientScope = (IScope)httpContextItems[ScopeItemKey];
if (ambientScope != null) RegisterContext(ambientScope, null);
// then register the new value
var scope = value as IScope;
if (scope != null) RegisterContext(scope, "http");
}
#endif
if (value == null)
httpContextItems.Remove(key);
else
httpContextItems[key] = value;
return true;
}
#endregion
#region Ambient Context
internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext";
private static ScopeContext CallContextContextValue
internal static ScopeContext AmbientContextInternal
{
get { return (ScopeContext)CallContext.LogicalGetData(ContextItemKey); }
set
get
{
if (value == null) CallContext.FreeNamedDataSlot(ContextItemKey);
else CallContext.LogicalSetData(ContextItemKey, value);
// try http context, fallback onto call context
var value = GetHttpContextObject<ScopeContext>(ContextItemKey, false);
return value ?? GetCallContextObject<ScopeContext>(ContextItemKey);
}
}
private static ScopeContext HttpContextContextValue
{
get { return (ScopeContext)HttpContext.Current.Items[ContextItemKey]; }
set
{
if (value == null)
HttpContext.Current.Items.Remove(ContextItemKey);
else
HttpContext.Current.Items[ContextItemKey] = value;
}
}
// clear both
SetHttpContextObject(ContextItemKey, null, false);
SetCallContextObject(ContextItemKey, null);
if (value == null) return;
private static ScopeContext StaticAmbientContext
{
get { return HttpContext.Current == null ? CallContextContextValue : HttpContextContextValue; }
set
{
if (HttpContext.Current == null)
CallContextContextValue = value;
else
HttpContextContextValue = value;
// set http/call context
if (SetHttpContextObject(ContextItemKey, value, false) == false)
SetCallContextObject(ContextItemKey, value);
}
}
/// <inheritdoc />
public ScopeContext AmbientContext
{
get { return StaticAmbientContext; }
set { StaticAmbientContext = value; }
get { return AmbientContextInternal; }
}
#endregion
@@ -109,86 +263,84 @@ namespace Umbraco.Core.Scoping
// only 1 instance which can be disposed and disposed again
private static readonly ScopeReference StaticScopeReference = new ScopeReference(new ScopeProvider(null));
private static IScopeInternal CallContextValue
internal static IScopeInternal AmbientScopeInternal
{
get { return (IScopeInternal) CallContext.LogicalGetData(ScopeItemKey); }
set
get
{
#if DEBUG_SCOPES
// manage the 'context' that contains the scope (null, "http" or "lcc")
var ambientScope = (IScope) CallContext.LogicalGetData(ScopeItemKey);
if (ambientScope != null) RegisterContext(ambientScope, null);
if (value != null) RegisterContext(value, "lcc");
#endif
if (value == null) CallContext.FreeNamedDataSlot(ScopeItemKey);
else CallContext.LogicalSetData(ScopeItemKey, value);
// try http context, fallback onto call context
var value = GetHttpContextObject<IScopeInternal>(ScopeItemKey, false);
return value ?? GetCallContextObject<IScopeInternal>(ScopeItemKey);
}
}
private static IScopeInternal HttpContextValue
{
get { return (IScopeInternal) HttpContext.Current.Items[ScopeItemKey]; }
set
{
#if DEBUG_SCOPES
// manage the 'context' that contains the scope (null, "http" or "lcc")
var ambientScope = (IScope) HttpContext.Current.Items[ScopeItemKey];
if (ambientScope != null) RegisterContext(ambientScope, null);
if (value != null) RegisterContext(value, "http");
#endif
if (value == null)
{
HttpContext.Current.Items.Remove(ScopeItemKey);
HttpContext.Current.Items.Remove(ScopeRefItemKey);
}
else
{
HttpContext.Current.Items[ScopeItemKey] = value;
if (HttpContext.Current.Items[ScopeRefItemKey] == null)
HttpContext.Current.Items[ScopeRefItemKey] = StaticScopeReference;
}
}
}
// clear both
SetHttpContextObject(ScopeItemKey, null, false);
SetHttpContextObject(ScopeRefItemKey, null, false);
SetCallContextObject(ScopeItemKey, null);
if (value == null) return;
private static IScopeInternal StaticAmbientScope
{
get { return HttpContext.Current == null ? CallContextValue : HttpContextValue; }
set
{
if (HttpContext.Current == null)
CallContextValue = value;
// set http/call context
if (value.CallContext == false && SetHttpContextObject(ScopeItemKey, value, false))
SetHttpContextObject(ScopeRefItemKey, StaticScopeReference);
else
HttpContextValue = value;
SetCallContextObject(ScopeItemKey, value);
}
}
/// <inheritdoc />
public IScopeInternal AmbientScope
{
get { return StaticAmbientScope; }
set { StaticAmbientScope = value; }
get { return AmbientScopeInternal; }
internal set { AmbientScopeInternal = value; }
}
/// <inheritdoc />
public IScopeInternal GetAmbientOrNoScope()
{
return AmbientScope ?? (AmbientScope = new NoScope(this));
return AmbientScope ?? (AmbientScope = new NoScope(this));
}
#endregion
public void SetAmbient(IScopeInternal scope, ScopeContext context = null)
{
// clear all
SetHttpContextObject(ScopeItemKey, null, false);
SetHttpContextObject(ScopeRefItemKey, null, false);
SetCallContextObject(ScopeItemKey, null);
SetHttpContextObject(ContextItemKey, null, false);
SetCallContextObject(ContextItemKey, null);
if (scope == null)
{
if (context != null)
throw new ArgumentException("Must be null if scope is null.", "context");
return;
}
if (scope.CallContext == false && SetHttpContextObject(ScopeItemKey, scope, false))
{
SetHttpContextObject(ScopeRefItemKey, StaticScopeReference);
SetHttpContextObject(ContextItemKey, context);
}
else
{
SetCallContextObject(ScopeItemKey, scope);
SetCallContextObject(ContextItemKey, context);
}
}
/// <inheritdoc />
public IScope CreateDetachedScope(
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null)
{
return new Scope(this, true, null, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
return new Scope(this, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
}
/// <inheritdoc />
public void AttachScope(IScope other)
public void AttachScope(IScope other, bool callContext = false)
{
var otherScope = other as Scope;
if (otherScope == null)
@@ -197,10 +349,15 @@ namespace Umbraco.Core.Scoping
if (otherScope.Detachable == false)
throw new ArgumentException("Not a detachable scope.");
if (otherScope.Attached)
throw new InvalidOperationException("Already attached.");
otherScope.Attached = true;
otherScope.OrigScope = AmbientScope;
otherScope.OrigContext = AmbientContext;
AmbientScope = otherScope;
AmbientContext = otherScope.Context;
otherScope.CallContext = callContext;
SetAmbient(otherScope, otherScope.Context);
}
/// <inheritdoc />
@@ -221,10 +378,10 @@ namespace Umbraco.Core.Scoping
if (scope.Detachable == false)
throw new InvalidOperationException("Ambient scope is not detachable.");
AmbientScope = scope.OrigScope;
AmbientContext = scope.OrigContext;
SetAmbient(scope.OrigScope, scope.OrigContext);
scope.OrigScope = null;
scope.OrigContext = null;
scope.Attached = false;
return scope;
}
@@ -232,16 +389,19 @@ namespace Umbraco.Core.Scoping
public IScope CreateScope(
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
bool? scopeFileSystems = null)
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false)
{
var ambient = AmbientScope;
if (ambient == null)
{
var context = AmbientContext == null ? new ScopeContext() : null;
var scope = new Scope(this, false, context, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
if (AmbientContext == null) AmbientContext = context; // assign only if scope creation did not throw!
return AmbientScope = scope;
var ambientContext = AmbientContext;
var newContext = ambientContext == null ? new ScopeContext() : null;
var scope = new Scope(this, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
// assign only if scope creation did not throw!
SetAmbient(scope, newContext ?? ambientContext);
return scope;
}
// replace noScope with a real one
@@ -255,16 +415,20 @@ namespace Umbraco.Core.Scoping
var database = noScope.DatabaseOrNull;
if (database != null && database.InTransaction)
throw new Exception("NoScope is in a transaction.");
var context = AmbientContext == null ? new ScopeContext() : null;
var scope = new Scope(this, noScope, context, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
if (AmbientContext == null) AmbientContext = context; // assign only if scope creation did not throw!
return AmbientScope = scope;
var ambientContext = AmbientContext;
var newContext = ambientContext == null ? new ScopeContext() : null;
var scope = new Scope(this, noScope, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
// assign only if scope creation did not throw!
SetAmbient(scope, newContext ?? ambientContext);
return scope;
}
var ambientScope = ambient as Scope;
if (ambientScope == null) throw new Exception("Ambient scope is not a Scope instance.");
return AmbientScope = new Scope(this, ambientScope, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
var nested = new Scope(this, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
SetAmbient(nested, AmbientContext);
return nested;
}
/// <inheritdoc />
@@ -309,7 +473,7 @@ namespace Umbraco.Core.Scoping
// all scope instances that are currently beeing tracked
private static readonly object StaticScopeInfosLock = new object();
private static readonly List<ScopeInfo> StaticScopeInfos = new List<ScopeInfo>();
private static readonly Dictionary<IScope, ScopeInfo> StaticScopeInfos = new Dictionary<IScope, ScopeInfo>();
public IEnumerable<ScopeInfo> ScopeInfos
{
@@ -317,37 +481,53 @@ namespace Umbraco.Core.Scoping
{
lock (StaticScopeInfosLock)
{
return StaticScopeInfos.ToArray(); // capture in an array
return StaticScopeInfos.Values.ToArray(); // capture in an array
}
}
}
public ScopeInfo GetScopeInfo(IScope scope)
{
lock (StaticScopeInfosLock)
{
ScopeInfo scopeInfo;
return StaticScopeInfos.TryGetValue(scope, out scopeInfo) ? scopeInfo : null;
}
}
//private static void Log(string message, UmbracoDatabase database)
//{
// LogHelper.Debug<ScopeProvider>(message + " (" + (database == null ? "" : database.InstanceSid) + ").");
//}
// register a scope and capture its ctor stacktrace
public void RegisterScope(IScope scope)
{
lock (StaticScopeInfosLock)
{
if (StaticScopeInfos.Any(x => x.Scope == scope)) throw new Exception("oops: already registered.");
StaticScopeInfos.Add(new ScopeInfo(scope, Environment.StackTrace));
if (StaticScopeInfos.ContainsKey(scope)) throw new Exception("oops: already registered.");
//Logging.LogHelper.Debug<ScopeProvider>("Register " + scope.InstanceId.ToString("N").Substring(0, 8));
StaticScopeInfos[scope] = new ScopeInfo(scope, Environment.StackTrace);
}
}
// 'context' that contains the scope (null, "http" or "lcc")
// register that a scope is in a 'context'
// 'context' that contains the scope (null, "http" or "call")
public static void RegisterContext(IScope scope, string context)
{
lock (StaticScopeInfosLock)
{
var info = StaticScopeInfos.FirstOrDefault(x => x.Scope == scope);
ScopeInfo info;
if (StaticScopeInfos.TryGetValue(scope, out info) == false) info = null;
if (info == null)
{
if (context == null) return;
throw new Exception("oops: unregistered scope.");
}
//Logging.LogHelper.Debug<ScopeProvider>("Register context " + (context ?? "null") + " for " + scope.InstanceId.ToString("N").Substring(0, 8));
if (context == null) info.NullStack = Environment.StackTrace;
//if (context == null)
// Logging.LogHelper.Debug<ScopeProvider>("STACK\r\n" + info.NullStack);
info.Context = context;
}
}
@@ -356,11 +536,12 @@ namespace Umbraco.Core.Scoping
{
lock (StaticScopeInfosLock)
{
var info = StaticScopeInfos.FirstOrDefault(x => x.Scope == scope);
if (info != null)
if (StaticScopeInfos.ContainsKey(scope))
{
// enable this by default
StaticScopeInfos.Remove(info);
//Console.WriteLine("unregister " + scope.InstanceId.ToString("N").Substring(0, 8));
StaticScopeInfos.Remove(scope);
//Logging.LogHelper.Debug<ScopeProvider>("Remove " + scope.InstanceId.ToString("N").Substring(0, 8));
// instead, enable this to keep *all* scopes
// beware, there can be a lot of scopes!
@@ -176,7 +176,7 @@ namespace Umbraco.Core.Security
AllowRefresh = true,
IssuedUtc = nowUtc,
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
}, userIdentity, rememberBrowserIdentity);
}, userIdentity, rememberBrowserIdentity);
}
else
{
@@ -189,6 +189,10 @@ namespace Umbraco.Core.Security
}, userIdentity);
}
//track the last login date
user.LastLoginDateUtc = DateTime.UtcNow;
await UserManager.UpdateAsync(user);
_logger.WriteCore(TraceEventType.Information, 0,
string.Format(
"Login attempt succeeded for username {0} from IP address {1}",
@@ -625,6 +625,12 @@ namespace Umbraco.Core.Security
var anythingChanged = false;
//don't assign anything if nothing has changed as this will trigger
//the track changes of the model
if ((user.LastLoginDate != default(DateTime) && identityUser.LastLoginDateUtc.HasValue == false)
|| identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)
{
anythingChanged = true;
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
}
if (user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
{
anythingChanged = true;
+7 -14
View File
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Services
uow.Events.Dispatch(Created, this, new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId));
//Created.RaiseEvent(new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId), this, uow.Events);
// fixme
// fixme
var auditRepo = RepositoryFactory.CreateAuditRepository(uow);
auditRepo.AddOrUpdate(new AuditItem(content.Id, string.Format("Content '{0}' was created", name), AuditType.New, content.CreatorId));
uow.Commit();
@@ -1051,9 +1051,9 @@ namespace Umbraco.Core.Services
UnPublish(content, userId);
}
content.WriterId = userId;
content.ChangeTrashedState(true);
content.ChangeTrashedState(true);
repository.AddOrUpdate(content);
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
foreach (var descendant in descendants)
{
@@ -1063,14 +1063,14 @@ namespace Umbraco.Core.Services
descendant.ChangeTrashedState(true, descendant.ParentId);
repository.AddOrUpdate(descendant);
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
}
uow.Commit();
uow.Events.Dispatch(Trashed, this, new MoveEventArgs<IContent>(false, evtMsgs, moveInfo.ToArray()), "Trashed");
}
Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
return OperationStatus.Success(evtMsgs);
@@ -1285,9 +1285,6 @@ namespace Umbraco.Core.Services
var args = new DeleteEventArgs<IContent>(content, false, evtMsgs);
uow.Events.Dispatch(Deleted, this, args, "Deleted"); // fixme why the event name?!
//remove any flagged media files
repository.DeleteMediaFiles(args.MediaFilesToDelete);
}
Audit(AuditType.Delete, "Delete Content performed by user", userId, content.Id);
@@ -1335,7 +1332,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Deletes all content of the specified types. All Descendants of deleted content that is not of these types is moved to Recycle Bin.
/// </summary>
/// </summary>
/// <param name="contentTypeIds">Id of the <see cref="IContentType"/></param>
/// <param name="userId">Optional Id of the user issueing the delete operation</param>
public void DeleteContentOfTypes(IEnumerable<int> contentTypeIds, int userId = 0)
@@ -1344,7 +1341,7 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateContentRepository(uow);
//track the 'root' items of the collection of nodes discovered to delete, we need to use
//these items to lookup descendants that are not of this doc type so they can be transfered
//to the recycle bin
@@ -1559,9 +1556,6 @@ namespace Umbraco.Core.Services
success = repository.EmptyRecycleBin();
if (success)
repository.DeleteMediaFiles(files);
uow.Events.Dispatch(EmptiedRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files, success));
uow.Commit();
}
@@ -1610,7 +1604,6 @@ namespace Umbraco.Core.Services
// fixme mess!
using (var scope = UowProvider.ScopeProvider.CreateScope())
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Copying, this, new CopyEventArgs<IContent>(content, copy, parentId)))
@@ -756,6 +756,7 @@ namespace Umbraco.Core.Services
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
{
scope.Events.Dispatch(SavedContentType, this, new SaveEventArgs<IContentType>(contentType, false));
scope.Complete();
}
Audit(AuditType.Save, "Save ContentType performed by user", userId, contentType.Id);
}
@@ -800,8 +801,9 @@ namespace Umbraco.Core.Services
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
{
scope.Events.Dispatch(SavedContentType, this, new SaveEventArgs<IContentType>(asArray, false));
scope.Complete();
}
Audit(AuditType.Save, "Save ContentTypes performed by user", userId, -1);
Audit(AuditType.Save, "Save ContentTypes performed by user", userId, -1);
}
/// <summary>
@@ -1211,6 +1213,7 @@ namespace Umbraco.Core.Services
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
{
scope.Events.Dispatch(SavedMediaType, this, new SaveEventArgs<IMediaType>(mediaType, false));
scope.Complete();
}
Audit(AuditType.Save, "Save MediaType performed by user", userId, mediaType.Id);
}
@@ -1255,6 +1258,7 @@ namespace Umbraco.Core.Services
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
{
scope.Events.Dispatch(SavedMediaType, this, new SaveEventArgs<IMediaType>(asArray, false));
scope.Complete();
}
Audit(AuditType.Save, "Save MediaTypes performed by user", userId, -1);
}
+20
View File
@@ -735,6 +735,26 @@ namespace Umbraco.Core.Services
return empty.Union(files.Except(empty));
}
public void CreatePartialViewFolder(string folderPath)
{
var uow = _fileUowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreatePartialViewRepository(uow))
{
((PartialViewRepository)repository).AddFolder(folderPath);
uow.Commit();
}
}
public void CreatePartialViewMacroFolder(string folderPath)
{
var uow = _fileUowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreatePartialViewMacroRepository(uow))
{
((PartialViewMacroRepository)repository).AddFolder(folderPath);
uow.Commit();
}
}
public void DeletePartialViewFolder(string folderPath)
{
using (var uow = _fileUowProvider.GetUnitOfWork())
@@ -11,6 +11,8 @@ namespace Umbraco.Core.Services
public interface IFileService : IService
{
IEnumerable<string> GetPartialViewSnippetNames(params string[] filterNames);
void CreatePartialViewFolder(string folderPath);
void CreatePartialViewMacroFolder(string folderPath);
void DeletePartialViewFolder(string folderPath);
void DeletePartialViewMacroFolder(string folderPath);
IPartialView GetPartialView(string path);
@@ -831,9 +831,6 @@ namespace Umbraco.Core.Services
var args = new DeleteEventArgs<IMedia>(media, false, evtMsgs);
uow.Events.Dispatch(Deleted, this, args);
//remove any flagged media files
repository.DeleteMediaFiles(args.MediaFilesToDelete);
}
Audit(AuditType.Delete, "Delete Media performed by user", userId, media.Id);
@@ -958,9 +955,6 @@ namespace Umbraco.Core.Services
success = repository.EmptyRecycleBin();
// FIXME shouldn't we commit here?!
uow.Events.Dispatch(EmptiedRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files, success));
if (success)
repository.DeleteMediaFiles(files);
}
}
Audit(AuditType.Delete, "Empty Media Recycle Bin performed by user", 0, -21);
+9 -12
View File
@@ -45,7 +45,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Gets the default MemberType alias
/// </summary>
/// <remarks>By default we'll return the 'writer', but we need to check it exists. If it doesn't we'll
/// <remarks>By default we'll return the 'writer', but we need to check it exists. If it doesn't we'll
/// return the first type that is not an admin, otherwise if there's only one we will return that one.</remarks>
/// <returns>Alias of the default MemberType</returns>
public string GetDefaultMemberType()
@@ -86,7 +86,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method
/// </summary>
/// <remarks>This method exists so that Umbraco developers can use one entry point to create/update
/// <remarks>This method exists so that Umbraco developers can use one entry point to create/update
/// Members if they choose to. </remarks>
/// <param name="member">The Member to save the password for</param>
/// <param name="password">The password to encrypt and save</param>
@@ -776,7 +776,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Creates and persists a Member
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
/// <remarks>Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
@@ -792,7 +792,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Creates and persists a Member
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
/// <remarks>Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
@@ -806,7 +806,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Creates and persists a Member
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
/// <remarks>Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
@@ -836,7 +836,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Creates and persists a Member
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
/// <remarks>Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
@@ -921,7 +921,7 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IMember"/></returns>
public IMember GetByUsername(string username)
{
//TODO: Somewhere in here, whether at this level or the repository level, we need to add
//TODO: Somewhere in here, whether at this level or the repository level, we need to add
// a caching mechanism since this method is used by all the membership providers and could be
// called quite a bit when dealing with members.
@@ -953,9 +953,6 @@ namespace Umbraco.Core.Services
var args = new DeleteEventArgs<IMember>(member, false);
uow.Events.Dispatch(Deleted, this, args);
//remove any flagged media files
repository.DeleteMediaFiles(args.MediaFilesToDelete);
}
}
@@ -963,7 +960,7 @@ namespace Umbraco.Core.Services
/// Saves an <see cref="IMember"/>
/// </summary>
/// <param name="entity"><see cref="IMember"/> to Save</param>
/// <param name="raiseEvents">Optional parameter to raise events.
/// <param name="raiseEvents">Optional parameter to raise events.
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
public void Save(IMember entity, bool raiseEvents = true)
{
@@ -1004,7 +1001,7 @@ namespace Umbraco.Core.Services
/// Saves a list of <see cref="IMember"/> objects
/// </summary>
/// <param name="entities"><see cref="IEnumerable{IMember}"/> to save</param>
/// <param name="raiseEvents">Optional parameter to raise events.
/// <param name="raiseEvents">Optional parameter to raise events.
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
public void Save(IEnumerable<IMember> entities, bool raiseEvents = true)
{
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Services
private readonly IMemberService _memberService;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMemberService memberService)
: base(provider, repositoryFactory, logger, eventMessagesFactory)
@@ -110,7 +110,7 @@ namespace Umbraco.Core.Services
public void Save(IEnumerable<IMemberType> memberTypes, int userId = 0)
{
var asArray = memberTypes.ToArray();
using (new WriteLock(Locker))
{
using (var uow = UowProvider.GetUnitOfWork())
@@ -137,7 +137,7 @@ namespace Umbraco.Core.Services
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMemberType>(asArray, false));
}
}
}
public void Delete(IMemberType memberType, int userId = 0)
@@ -167,7 +167,7 @@ namespace Umbraco.Core.Services
public void Delete(IEnumerable<IMemberType> memberTypes, int userId = 0)
{
var asArray = memberTypes.ToArray();
using (new WriteLock(Locker))
{
using (var scope = UowProvider.ScopeProvider.CreateScope())
+24 -1
View File
@@ -1716,6 +1716,24 @@ namespace Umbraco.Core.Services
#region Package Building
#endregion
/// <summary>
/// This method can be used to trigger the 'ImportedPackage' event when a package is installed by something else but this service.
/// </summary>
/// <param name="args"></param>
internal static void OnImportedPackage(ImportPackageEventArgs<InstallationSummary> args)
{
ImportedPackage.RaiseEvent(args, null);
}
/// <summary>
/// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service.
/// </summary>
/// <param name="args"></param>
internal static void OnUninstalledPackage(UninstallPackageEventArgs<UninstallationSummary> args)
{
UninstalledPackage.RaiseEvent(args, null);
}
#region Event Handlers
/// <summary>
/// Occurs before Importing Content
@@ -1876,10 +1894,15 @@ namespace Umbraco.Core.Services
internal static event TypedEventHandler<IPackagingService, ImportPackageEventArgs<string>> ImportingPackage;
/// <summary>
/// Occurs after a apckage is imported
/// Occurs after a package is imported
/// </summary>
internal static event TypedEventHandler<IPackagingService, ImportPackageEventArgs<InstallationSummary>> ImportedPackage;
/// <summary>
/// Occurs after a package is uninstalled
/// </summary>
internal static event TypedEventHandler<IPackagingService, UninstallPackageEventArgs<UninstallationSummary>> UninstalledPackage;
#endregion
}
}
+2 -2
View File
@@ -215,7 +215,7 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork(commit: true))
{
var repository = RepositoryFactory.CreateRelationRepository(uow);
var query = new Query<IRelation>().Where(x => x.ChildId == id || x.ParentId == id);
var query = new Query<IRelation>().Where(x => x.ParentId == id || x.ChildId == id);
return repository.GetByQuery(query);
}
}
@@ -230,7 +230,7 @@ namespace Umbraco.Core.Services
if (relationType == null) return Enumerable.Empty<IRelation>();
var relationRepo = RepositoryFactory.CreateRelationRepository(uow);
var query = new Query<IRelation>().Where(x => (x.ChildId == id || x.ParentId == id) && x.RelationTypeId == relationType.Id);
var query = new Query<IRelation>().Where(x => (x.ParentId == id || x.ChildId == id) && x.RelationTypeId == relationType.Id);
return relationRepo.GetByQuery(query);
}
}
+21
View File
@@ -425,7 +425,28 @@ namespace Umbraco.Core
assemblyName.FullName.StartsWith("App_Code.") ? "App_Code" : assemblyName.Name);
}
/// <summary>
/// If the given <paramref name="type"/> is an array or some other collection
/// comprised of 0 or more instances of a "subtype", get that type
/// </summary>
/// <param name="type">the source type</param>
/// <returns></returns>
internal static Type GetEnumeratedType(this Type type)
{
if (typeof(IEnumerable).IsAssignableFrom(type) == false)
return null;
// provided by Array
var elType = type.GetElementType();
if (null != elType) return elType;
// otherwise provided by collection
var elTypes = type.GetGenericArguments();
if (elTypes.Length > 0) return elTypes[0];
// otherwise is not an 'enumerated' type
return null;
}
}
}
+55 -34
View File
@@ -1,7 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Umbraco.Core.Deploy;
namespace Umbraco.Core
@@ -10,9 +12,10 @@ namespace Umbraco.Core
/// Represents an entity identifier.
/// </summary>
/// <remarks>An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document.</remarks>
[TypeConverter(typeof(UdiTypeConverter))]
public abstract class Udi : IComparable<Udi>
{
private static readonly Dictionary<string, UdiType> UdiTypes = new Dictionary<string, UdiType>();
private static readonly Lazy<Dictionary<string, UdiType>> UdiTypes;
private static readonly ConcurrentDictionary<string, Udi> RootUdis = new ConcurrentDictionary<string, Udi>();
internal readonly Uri UriValue; // internal for UdiRange
@@ -39,32 +42,50 @@ namespace Umbraco.Core
static Udi()
{
// for tests etc.
UdiTypes[Constants.DeployEntityType.AnyGuid] = UdiType.GuidUdi;
UdiTypes[Constants.DeployEntityType.AnyString] = UdiType.StringUdi;
// we don't have connectors for these...
UdiTypes[Constants.DeployEntityType.Member] = UdiType.GuidUdi;
UdiTypes[Constants.DeployEntityType.MemberGroup] = UdiType.GuidUdi;
// fixme - or inject from...?
// there is no way we can get the "registered" service connectors, as registration
// happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
// just pick every service connectors - just making sure that not two of them
// would register the same entity type, with different udi types (would not make
// much sense anyways).
var connectors = PluginManager.Current.ResolveTypes<IServiceConnector>();
foreach (var connector in connectors)
UdiTypes = new Lazy<Dictionary<string, UdiType>>(() =>
{
var attrs = connector.GetCustomAttributes<UdiDefinitionAttribute>(false);
foreach (var attr in attrs)
var result = new Dictionary<string, UdiType>();
// known types:
foreach (var fi in typeof(Constants.UdiEntityType).GetFields(BindingFlags.Public | BindingFlags.Static))
{
UdiType udiType;
if (UdiTypes.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType)
throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
UdiTypes[attr.EntityType] = attr.UdiType;
// IsLiteral determines if its value is written at
// compile time and not changeable
// IsInitOnly determine if the field can be set
// in the body of the constructor
// for C# a field which is readonly keyword would have both true
// but a const field would have only IsLiteral equal to true
if (fi.IsLiteral && fi.IsInitOnly == false)
{
var udiType = fi.GetCustomAttribute<Constants.UdiTypeAttribute>();
if (udiType == null)
throw new InvalidOperationException("All Constants listed in UdiEntityType must be attributed with " + typeof(Constants.UdiTypeAttribute));
result[fi.GetValue(null).ToString()] = udiType.UdiType;
}
}
}
// Scan for unknown UDI types
// there is no way we can get the "registered" service connectors, as registration
// happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
// just pick every service connectors - just making sure that not two of them
// would register the same entity type, with different udi types (would not make
// much sense anyways).
var connectors = PluginManager.Current.ResolveTypes<IServiceConnector>();
foreach (var connector in connectors)
{
var attrs = connector.GetCustomAttributes<UdiDefinitionAttribute>(false);
foreach (var attr in attrs)
{
UdiType udiType;
if (result.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType)
throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
result[attr.EntityType] = attr.UdiType;
}
}
return result;
});
}
/// <summary>
@@ -105,8 +126,8 @@ namespace Umbraco.Core
udi = null;
Uri uri;
if (!Uri.IsWellFormedUriString(s, UriKind.Absolute)
|| !Uri.TryCreate(s, UriKind.Absolute, out uri))
if (Uri.IsWellFormedUriString(s, UriKind.Absolute) == false
|| Uri.TryCreate(s, UriKind.Absolute, out uri) == false)
{
if (tryParse) return false;
throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
@@ -114,7 +135,7 @@ namespace Umbraco.Core
var entityType = uri.Host;
UdiType udiType;
if (!UdiTypes.TryGetValue(entityType, out udiType))
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
{
if (tryParse) return false;
throw new FormatException(string.Format("Unknown entity type \"{0}\".", entityType));
@@ -128,7 +149,7 @@ namespace Umbraco.Core
return true;
}
Guid guid;
if (!Guid.TryParse(path, out guid))
if (Guid.TryParse(path, out guid) == false)
{
if (tryParse) return false;
throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
@@ -150,7 +171,7 @@ namespace Umbraco.Core
return RootUdis.GetOrAdd(entityType, x =>
{
UdiType udiType;
if (!UdiTypes.TryGetValue(x, out udiType))
if (UdiTypes.Value.TryGetValue(x, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType));
return udiType == UdiType.StringUdi
? (Udi)new StringUdi(entityType, string.Empty)
@@ -177,7 +198,7 @@ namespace Umbraco.Core
public static Udi Create(string entityType, string id)
{
UdiType udiType;
if (!UdiTypes.TryGetValue(entityType, out udiType))
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentException("Value cannot be null or whitespace.", "id");
@@ -196,7 +217,7 @@ namespace Umbraco.Core
public static Udi Create(string entityType, Guid id)
{
UdiType udiType;
if (!UdiTypes.TryGetValue(entityType, out udiType))
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
if (udiType != UdiType.GuidUdi)
throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have guid udis.", entityType));
@@ -208,7 +229,7 @@ namespace Umbraco.Core
internal static Udi Create(Uri uri)
{
UdiType udiType;
if (!UdiTypes.TryGetValue(uri.Host, out udiType))
if (UdiTypes.Value.TryGetValue(uri.Host, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri");
if (udiType == UdiType.GuidUdi)
return new GuidUdi(uri);
@@ -219,7 +240,7 @@ namespace Umbraco.Core
public void EnsureType(params string[] validTypes)
{
if (!validTypes.Contains(EntityType))
if (validTypes.Contains(EntityType) == false)
throw new Exception(string.Format("Unexpected entity type \"{0}\".", EntityType));
}
@@ -260,7 +281,7 @@ namespace Umbraco.Core
public static bool operator !=(Udi udi1, Udi udi2)
{
return !(udi1 == udi2);
return (udi1 == udi2) == false;
}
}
@@ -7,52 +7,82 @@ namespace Umbraco.Core
public static partial class Constants
{
/// <summary>
/// Defines well-known entity types.
/// </summary>
/// <remarks>Well-known entity types are those that Deploy already knows about,
/// but entity types are strings and so can be extended beyond what is defined here.</remarks>
public static class DeployEntityType
public static class UdiEntityType
{
[UdiType(UdiType.Unknown)]
public const string Unknown = "unknown";
// guid entity types
[UdiType(UdiType.GuidUdi)]
public const string AnyGuid = "any-guid"; // that one is for tests
[UdiType(UdiType.GuidUdi)]
public const string Document = "document";
[UdiType(UdiType.GuidUdi)]
public const string Media = "media";
[UdiType(UdiType.GuidUdi)]
public const string Member = "member";
[UdiType(UdiType.GuidUdi)]
public const string DictionaryItem = "dictionary-item";
[UdiType(UdiType.GuidUdi)]
public const string Macro = "macro";
[UdiType(UdiType.GuidUdi)]
public const string Template = "template";
[UdiType(UdiType.GuidUdi)]
public const string DocumentType = "document-type";
[UdiType(UdiType.GuidUdi)]
public const string DocumentTypeContainer = "document-type-container";
[UdiType(UdiType.GuidUdi)]
public const string MediaType = "media-type";
[UdiType(UdiType.GuidUdi)]
public const string MediaTypeContainer = "media-type-container";
[UdiType(UdiType.GuidUdi)]
public const string DataType = "data-type";
[UdiType(UdiType.GuidUdi)]
public const string DataTypeContainer = "data-type-container";
[UdiType(UdiType.GuidUdi)]
public const string MemberType = "member-type";
[UdiType(UdiType.GuidUdi)]
public const string MemberGroup = "member-group";
[UdiType(UdiType.GuidUdi)]
public const string RelationType = "relation-type";
// forms
[UdiType(UdiType.GuidUdi)]
public const string FormsForm = "forms-form";
public const string FormsWorkflow = "forms-workflow";
public const string FormsRecord = "forms-record";
[UdiType(UdiType.GuidUdi)]
public const string FormsPreValue = "forms-prevalue";
[UdiType(UdiType.GuidUdi)]
public const string FormsDataSource = "forms-datasource";
// string entity types
[UdiType(UdiType.StringUdi)]
public const string AnyString = "any-string"; // that one is for tests
[UdiType(UdiType.StringUdi)]
public const string MediaFile = "media-file";
[UdiType(UdiType.StringUdi)]
public const string TemplateFile = "template-file";
[UdiType(UdiType.StringUdi)]
public const string Script = "script";
[UdiType(UdiType.StringUdi)]
public const string Stylesheet = "stylesheet";
[UdiType(UdiType.StringUdi)]
public const string PartialView = "partial-view";
[UdiType(UdiType.StringUdi)]
public const string PartialViewMacro = "partial-view-macro";
[UdiType(UdiType.StringUdi)]
public const string Xslt = "xslt";
public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType)
@@ -89,10 +119,10 @@ namespace Umbraco.Core
return RelationType;
case UmbracoObjectTypes.FormsForm:
return FormsForm;
case UmbracoObjectTypes.FormsWorkflow:
return FormsWorkflow;
case UmbracoObjectTypes.FormsRecord:
return FormsRecord;
case UmbracoObjectTypes.FormsPreValue:
return FormsPreValue;
case UmbracoObjectTypes.FormsDataSource:
return FormsDataSource;
}
throw new NotSupportedException(string.Format("UmbracoObjectType \"{0}\" does not have a matching EntityType.", umbracoObjectType));
}
@@ -131,16 +161,25 @@ namespace Umbraco.Core
return UmbracoObjectTypes.RelationType;
case FormsForm:
return UmbracoObjectTypes.FormsForm;
case FormsWorkflow:
return UmbracoObjectTypes.FormsWorkflow;
case FormsRecord:
return UmbracoObjectTypes.FormsRecord;
case FormsPreValue:
return UmbracoObjectTypes.FormsPreValue;
case FormsDataSource:
return UmbracoObjectTypes.FormsDataSource;
}
throw new NotSupportedException(
string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType));
}
}
[AttributeUsage(AttributeTargets.Field)]
internal class UdiTypeAttribute : Attribute
{
public UdiType UdiType { get; private set; }
public UdiTypeAttribute(UdiType udiType)
{
UdiType = udiType;
}
}
}
}
+42 -30
View File
@@ -17,7 +17,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this ITemplate entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.Template, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.Template, entity.Key).EnsureClosed();
}
/// <summary>
@@ -28,7 +28,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IContentType entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.DocumentType, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.DocumentType, entity.Key).EnsureClosed();
}
/// <summary>
@@ -39,7 +39,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IMediaType entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.MediaType, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.MediaType, entity.Key).EnsureClosed();
}
/// <summary>
@@ -50,7 +50,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IMemberType entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.MemberType, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.MemberType, entity.Key).EnsureClosed();
}
/// <summary>
@@ -61,7 +61,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IMemberGroup entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.MemberGroup, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.MemberGroup, entity.Key).EnsureClosed();
}
/// <summary>
@@ -74,9 +74,9 @@ namespace Umbraco.Core
if (entity == null) throw new ArgumentNullException("entity");
string type;
if (entity is IContentType) type = Constants.DeployEntityType.DocumentType;
else if (entity is IMediaType) type = Constants.DeployEntityType.MediaType;
else if (entity is IMemberType) type = Constants.DeployEntityType.MemberType;
if (entity is IContentType) type = Constants.UdiEntityType.DocumentType;
else if (entity is IMediaType) type = Constants.UdiEntityType.MediaType;
else if (entity is IMemberType) type = Constants.UdiEntityType.MemberType;
else throw new NotSupportedException(string.Format("Composition type {0} is not supported.", entity.GetType().FullName));
return new GuidUdi(type, entity.Key).EnsureClosed();
}
@@ -89,7 +89,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IDataTypeDefinition entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.DataType, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.DataType, entity.Key).EnsureClosed();
}
/// <summary>
@@ -103,11 +103,11 @@ namespace Umbraco.Core
string entityType;
if (entity.ContainedObjectType == Constants.ObjectTypes.DataTypeGuid)
entityType = Constants.DeployEntityType.DataTypeContainer;
entityType = Constants.UdiEntityType.DataTypeContainer;
else if (entity.ContainedObjectType == Constants.ObjectTypes.DocumentTypeGuid)
entityType = Constants.DeployEntityType.DocumentTypeContainer;
entityType = Constants.UdiEntityType.DocumentTypeContainer;
else if (entity.ContainedObjectType == Constants.ObjectTypes.MediaTypeGuid)
entityType = Constants.DeployEntityType.MediaTypeContainer;
entityType = Constants.UdiEntityType.MediaTypeContainer;
else
throw new NotSupportedException(string.Format("Contained object type {0} is not supported.", entity.ContainedObjectType));
return new GuidUdi(entityType, entity.Key).EnsureClosed();
@@ -121,7 +121,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IMedia entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.Media, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.Media, entity.Key).EnsureClosed();
}
/// <summary>
@@ -132,7 +132,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IContent entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.Document, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.Document, entity.Key).EnsureClosed();
}
/// <summary>
@@ -143,7 +143,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IMember entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.Member, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.Member, entity.Key).EnsureClosed();
}
/// <summary>
@@ -154,7 +154,7 @@ namespace Umbraco.Core
public static StringUdi GetUdi(this Stylesheet entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new StringUdi(Constants.DeployEntityType.Stylesheet, entity.Path.TrimStart('/')).EnsureClosed();
return new StringUdi(Constants.UdiEntityType.Stylesheet, entity.Path.TrimStart('/')).EnsureClosed();
}
/// <summary>
@@ -165,7 +165,7 @@ namespace Umbraco.Core
public static StringUdi GetUdi(this Script entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new StringUdi(Constants.DeployEntityType.Script, entity.Path.TrimStart('/')).EnsureClosed();
return new StringUdi(Constants.UdiEntityType.Script, entity.Path.TrimStart('/')).EnsureClosed();
}
/// <summary>
@@ -176,7 +176,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IDictionaryItem entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.DictionaryItem, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.DictionaryItem, entity.Key).EnsureClosed();
}
/// <summary>
@@ -187,7 +187,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IMacro entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.Macro, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.Macro, entity.Key).EnsureClosed();
}
/// <summary>
@@ -198,7 +198,7 @@ namespace Umbraco.Core
public static StringUdi GetUdi(this IPartialView entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new StringUdi(Constants.DeployEntityType.PartialView, entity.Path.TrimStart('/')).EnsureClosed();
return new StringUdi(Constants.UdiEntityType.PartialView, entity.Path.TrimStart('/')).EnsureClosed();
}
/// <summary>
@@ -209,7 +209,7 @@ namespace Umbraco.Core
public static StringUdi GetUdi(this IXsltFile entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new StringUdi(Constants.DeployEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed();
return new StringUdi(Constants.UdiEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed();
}
/// <summary>
@@ -222,9 +222,9 @@ namespace Umbraco.Core
if (entity == null) throw new ArgumentNullException("entity");
string type;
if (entity is IContent) type = Constants.DeployEntityType.Document;
else if (entity is IMedia) type = Constants.DeployEntityType.Media;
else if (entity is IMember) type = Constants.DeployEntityType.Member;
if (entity is IContent) type = Constants.UdiEntityType.Document;
else if (entity is IMedia) type = Constants.UdiEntityType.Media;
else if (entity is IMember) type = Constants.UdiEntityType.Member;
else throw new NotSupportedException(string.Format("ContentBase type {0} is not supported.", entity.GetType().FullName));
return new GuidUdi(type, entity.Key).EnsureClosed();
}
@@ -237,7 +237,7 @@ namespace Umbraco.Core
public static GuidUdi GetUdi(this IRelationType entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new GuidUdi(Constants.DeployEntityType.RelationType, entity.Key).EnsureClosed();
return new GuidUdi(Constants.UdiEntityType.RelationType, entity.Key).EnsureClosed();
}
/// <summary>
@@ -285,18 +285,30 @@ namespace Umbraco.Core
var member = entity as IMember;
if (member != null) return member.GetUdi();
var contentBase = entity as IContentBase;
if (contentBase != null) return contentBase.GetUdi();
var stylesheet = entity as Stylesheet;
if (stylesheet != null) return stylesheet.GetUdi();
var script = entity as Script;
if (script != null) return script.GetUdi();
var dictionaryItem = entity as IDictionaryItem;
if (dictionaryItem != null) return dictionaryItem.GetUdi();
var macro = entity as IMacro;
if (macro != null) return macro.GetUdi();
var partialView = entity as IPartialView;
if (partialView != null) return partialView.GetUdi();
var xsltFile = entity as IXsltFile;
if (xsltFile != null) return xsltFile.GetUdi();
var contentBase = entity as IContentBase;
if (contentBase != null) return contentBase.GetUdi();
var relationType = entity as IRelationType;
if (relationType != null) return relationType.GetUdi();
var dictionaryItem = entity as IDictionaryItem;
if (dictionaryItem != null) return dictionaryItem.GetUdi();
throw new NotSupportedException(string.Format("Entity type {0} is not supported.", entity.GetType().FullName));
}
}
+37
View File
@@ -0,0 +1,37 @@
using System;
using System.ComponentModel;
using System.Globalization;
namespace Umbraco.Core
{
/// <summary>
/// A custom type converter for UDI
/// </summary>
/// <remarks>
/// Primarily this is used so that WebApi can auto-bind a string parameter to a UDI instance
/// </remarks>
internal class UdiTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
Udi udi;
if (Udi.TryParse((string)value, out udi))
{
return udi;
}
}
return base.ConvertFrom(context, culture, value);
}
}
}
+11 -3
View File
@@ -163,6 +163,7 @@
<Compile Include="Cache\DeepCloneRuntimeCacheProvider.cs" />
<Compile Include="CodeAnnotations\FriendlyNameAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoObjectTypeAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoUdiTypeAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoWillObsoleteAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoExperimentalFeatureAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoProposedPublicAttribute.cs" />
@@ -289,7 +290,6 @@
<Compile Include="Configuration\UmbracoSettings\WebRoutingElement.cs" />
<Compile Include="Configuration\UmbracoVersion.cs" />
<Compile Include="Attempt.cs" />
<Compile Include="Constants-DeployEntityType.cs" />
<Compile Include="Constants-DeploySelector.cs" />
<Compile Include="Constants-Examine.cs" />
<Compile Include="Constants-Icons.cs" />
@@ -313,7 +313,10 @@
<Compile Include="Deploy\Direction.cs" />
<Compile Include="Deploy\IFileStore.cs" />
<Compile Include="Events\EventDefinitionFilter.cs" />
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
<Compile Include="OrderedHashSet.cs" />
<Compile Include="Events\UninstallPackageEventArgs.cs" />
<Compile Include="Models\GridValue.cs" />
<Compile Include="Deploy\IArtifact.cs" />
<Compile Include="Deploy\IArtifactSignature.cs" />
@@ -361,7 +364,6 @@
<Compile Include="Events\MoveEventInfo.cs" />
<Compile Include="Events\PublishEventArgs.cs" />
<Compile Include="Events\RecycleBinEventArgs.cs" />
<Compile Include="Events\EventsDispatchMode.cs" />
<Compile Include="Events\TransientMessagesFactory.cs" />
<Compile Include="Events\TypedEventHandler.cs" />
<Compile Include="Events\MoveEventArgs.cs" />
@@ -450,6 +452,7 @@
<Compile Include="Models\DoNotCloneAttribute.cs" />
<Compile Include="Models\IDomain.cs" />
<Compile Include="NamedUdiRange.cs" />
<Compile Include="Packaging\Models\UninstallationSummary.cs" />
<Compile Include="Persistence\BulkDataReader.cs" />
<Compile Include="Persistence\Constants-Locks.cs" />
<Compile Include="Persistence\DatabaseNodeLockExtensions.cs" />
@@ -479,7 +482,10 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddUniqueIdPropertyTypeGroupColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\RemoveParentIdPropertyTypeGroupColumn.cs" />
<Compile Include="Persistence\Mappers\TaskTypeMapper.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToCmsMemberLoginName.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexesToUmbracoRelationTables.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToUmbracoNodePath.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToUser2NodePermission.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddRelationTypeUniqueIdColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddMacroUniqueIdColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemovePropertyDataIdIndex.cs" />
@@ -558,7 +564,7 @@
<Compile Include="Publishing\UnPublishedStatusType.cs" />
<Compile Include="Publishing\UnPublishStatus.cs" />
<Compile Include="SafeCallContext.cs" />
<Compile Include="Scoping\ActionTime.cs" />
<Compile Include="Scoping\IInstanceIdentifiable.cs" />
<Compile Include="Scoping\IScope.cs" />
<Compile Include="Scoping\IScopeInternal.cs" />
<Compile Include="Scoping\IScopeProvider.cs" />
@@ -1435,9 +1441,11 @@
<Compile Include="TypeHelper.cs" />
<Compile Include="Udi.cs" />
<Compile Include="UdiDefinitionAttribute.cs" />
<Compile Include="UdiEntityType.cs" />
<Compile Include="UdiGetterExtensions.cs" />
<Compile Include="UdiRange.cs" />
<Compile Include="UdiType.cs" />
<Compile Include="UdiTypeConverter.cs" />
<Compile Include="UmbracoApplicationBase.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Constants-Applications.cs">
+9
View File
@@ -56,5 +56,14 @@ namespace Umbraco.Tests.IO
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.WebServices, true), IOHelper.MapPath(SystemDirectories.WebServices, false));
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Xslt, true), IOHelper.MapPath(SystemDirectories.Xslt, false));
}
[Test]
public void EnsurePathIsApplicationRootPrefixed()
{
//Assert
Assert.AreEqual("~/Views/Template.cshtml", IOHelper.EnsurePathIsApplicationRootPrefixed("Views/Template.cshtml"));
Assert.AreEqual("~/Views/Template.cshtml", IOHelper.EnsurePathIsApplicationRootPrefixed("/Views/Template.cshtml"));
Assert.AreEqual("~/Views/Template.cshtml", IOHelper.EnsurePathIsApplicationRootPrefixed("~/Views/Template.cshtml"));
}
}
}
@@ -441,7 +441,7 @@ namespace Umbraco.Tests.Persistence.Repositories
{
// Act
var exists = repository.Exists(1034); //Content picker
var exists = repository.Exists(1046); //Content picker
var doesntExist = repository.Exists(-80);
// Assert
@@ -121,6 +121,19 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine,
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Unique_CreatesUniqueNonClusteredIndex_Multi_Columnn()
{
var sqlSyntax = new SqlServerSyntaxProvider();
var createExpression = new CreateIndexExpression(DatabaseProviders.SqlServer, new[] { DatabaseProviders.SqlServer }, sqlSyntax)
{
Index = { Name = "IX_AB" }
};
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().OnColumn("B").Ascending().WithOptions().Unique();
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_AB] ON [TheTable] ([A],[B])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Clustered_CreatesClusteredIndex()
{
+9 -5
View File
@@ -2,6 +2,7 @@
using System.Data;
using System.Runtime.Remoting.Messaging;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Persistence;
using Umbraco.Core.Scoping;
using Umbraco.Tests.TestHelpers;
@@ -38,11 +39,14 @@ namespace Umbraco.Tests.Scoping
_database.BeginTransaction(); // opens and maintains a connection
// the test is leaking a scope with a non-null database
var contextScope = CallContext.LogicalGetData(ScopeProvider.ScopeItemKey);
Assert.IsNotNull(contextScope);
Assert.IsInstanceOf<NoScope>(CallContext.LogicalGetData(ScopeProvider.ScopeItemKey));
Assert.IsNotNull(((NoScope) contextScope).DatabaseOrNull);
Assert.AreSame(_database, ((NoScope)contextScope).DatabaseOrNull);
var contextGuid = CallContext.LogicalGetData(ScopeProvider.ScopeItemKey).AsGuid();
Assert.AreNotEqual(Guid.Empty, contextGuid);
// only if Core.DEBUG_SCOPES are defined
//var contextScope = DatabaseContext.ScopeProvider.CallContextObjects[contextGuid] as NoScope;
//Assert.IsNotNull(contextScope);
//Assert.IsNotNull(contextScope.DatabaseOrNull);
//Assert.AreSame(_database, contextScope.DatabaseOrNull);
// save the connection
_connection = _database.Connection;
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
@@ -8,7 +9,6 @@ using Umbraco.Core.Scoping;
namespace Umbraco.Tests.Scoping
{
[TestFixture]
public class ScopeEventDispatcherTests
{
@@ -21,11 +21,15 @@ namespace Umbraco.Tests.Scoping
DoThing3 = null;
}
[TestCase(EventsDispatchMode.PassThrough, true, true)]
[TestCase(EventsDispatchMode.PassThrough, true, false)]
[TestCase(EventsDispatchMode.PassThrough, false, true)]
[TestCase(EventsDispatchMode.PassThrough, false, false)]
public void PassThroughCancelable(EventsDispatchMode mode, bool cancel, bool complete)
[TestCase(false, true, true)]
[TestCase(false, true, false)]
[TestCase(false, false, true)]
[TestCase(false, false, false)]
[TestCase(true, true, true)]
[TestCase(true, true, false)]
[TestCase(true, false, true)]
[TestCase(true, false, false)]
public void EventsHandling(bool passive, bool cancel, bool complete)
{
var counter1 = 0;
var counter2 = 0;
@@ -34,7 +38,7 @@ namespace Umbraco.Tests.Scoping
DoThing2 += (sender, args) => { counter2++; };
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
using (var scope = scopeProvider.CreateScope(dispatchMode: mode))
using (var scope = scopeProvider.CreateScope(eventDispatcher: passive ? new PassiveEventDispatcher() : null))
{
var cancelled = scope.Events.DispatchCancelable(DoThing1, this, new SaveEventArgs<string>("test"));
if (cancelled == false)
@@ -43,22 +47,15 @@ namespace Umbraco.Tests.Scoping
scope.Complete();
}
var expected1 = mode == EventsDispatchMode.Passive ? 0 : 1;
var expected1 = passive ? 0 : 1;
Assert.AreEqual(expected1, counter1);
var expected2 = -1;
switch (mode)
{
case EventsDispatchMode.PassThrough:
expected2 = cancel ? 0 : 1;
break;
case EventsDispatchMode.Scope:
expected2 = cancel ? 0 : (complete ? 1 : 0);
break;
case EventsDispatchMode.Passive:
expected2 = 0;
break;
}
int expected2;
if (passive)
expected2 = 0;
else
expected2 = cancel ? 0 : (complete ? 1 : 0);
Assert.AreEqual(expected2, counter2);
}
@@ -70,7 +67,7 @@ namespace Umbraco.Tests.Scoping
DoThing3 += OnDoThingFail;
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.Passive))
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
{
scope.Events.Dispatch(DoThing1, this, new SaveEventArgs<string>("test"));
scope.Events.Dispatch(DoThing2, this, new SaveEventArgs<int>(0));
@@ -94,45 +91,14 @@ namespace Umbraco.Tests.Scoping
[TestCase(true)]
[TestCase(false)]
public void EventsDispatchmode_PassThrough(bool complete)
{
var counter = 0;
DoThing1 += (sender, args) => { counter++; };
DoThing2 += (sender, args) => { counter++; };
DoThing3 += (sender, args) => { counter++; };
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.PassThrough))
{
scope.Events.Dispatch(DoThing1, this, new SaveEventArgs<string>("test"));
scope.Events.Dispatch(DoThing2, this, new SaveEventArgs<int>(0));
scope.Events.Dispatch(DoThing3, this, new SaveEventArgs<decimal>(0));
// events have not been queued
Assert.IsEmpty(scope.Events.GetEvents(EventDefinitionFilter.All));
// events have been raised
Assert.AreEqual(3, counter);
if (complete)
scope.Complete();
}
// nothing has changed
Assert.AreEqual(3, counter);
}
[TestCase(true)]
[TestCase(false)]
public void EventsDispatchMode_Passive(bool complete)
public void EventsDispatching_Passive(bool complete)
{
DoThing1 += OnDoThingFail;
DoThing2 += OnDoThingFail;
DoThing3 += OnDoThingFail;
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.Passive))
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
{
scope.Events.Dispatch(DoThing1, this, new SaveEventArgs<string>("test"));
scope.Events.Dispatch(DoThing2, this, new SaveEventArgs<int>(0));
@@ -150,7 +116,7 @@ namespace Umbraco.Tests.Scoping
[TestCase(true)]
[TestCase(false)]
public void EventsDispatchMode_Scope(bool complete)
public void EventsDispatching_Scope(bool complete)
{
var counter = 0;
IScope ambientScope = null;
@@ -170,7 +136,7 @@ namespace Umbraco.Tests.Scoping
};
Guid guid;
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.Scope))
using (var scope = scopeProvider.CreateScope())
{
Assert.IsNotNull(scopeProvider.AmbientContext);
guid = scopeProvider.Context.Enlist("value", Guid.NewGuid, (c, o) => { });
@@ -181,6 +147,7 @@ namespace Umbraco.Tests.Scoping
// events have been queued
Assert.AreEqual(3, scope.Events.GetEvents(EventDefinitionFilter.All).Count());
Assert.AreEqual(0, counter);
if (complete)
scope.Complete();
@@ -215,5 +182,17 @@ namespace Umbraco.Tests.Scoping
public static event EventHandler<SaveEventArgs<int>> DoThing2;
public static event TypedEventHandler<ScopeEventDispatcherTests, SaveEventArgs<decimal>> DoThing3;
}
public class PassiveEventDispatcher : ScopeEventDispatcherBase
{
public PassiveEventDispatcher()
: base(false)
{ }
protected override void ScopeExitCompleted()
{
// do nothing
}
}
}
}
+123 -8
View File
@@ -1,10 +1,12 @@
using System;
using System.Collections;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Persistence;
using Umbraco.Core.Scoping;
using Umbraco.Tests.TestHelpers;
using Umbraco.Core.Events;
namespace Umbraco.Tests.Scoping
{
@@ -97,6 +99,54 @@ namespace Umbraco.Tests.Scoping
Assert.IsNull(scopeProvider.AmbientScope);
}
[Test]
public void NestedMigrateScope()
{
var scopeProvider = DatabaseContext.ScopeProvider;
Assert.IsNull(scopeProvider.AmbientScope);
var httpContextItems = new Hashtable();
ScopeProvider.HttpContextItemsGetter = () => httpContextItems;
try
{
using (var scope = scopeProvider.CreateScope())
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
Assert.AreSame(scope, httpContextItems[ScopeProvider.ScopeItemKey]);
// only if Core.DEBUG_SCOPES are defined
//Assert.IsEmpty(scopeProvider.CallContextObjects);
using (var nested = scopeProvider.CreateScope(callContext: true))
{
Assert.IsInstanceOf<Scope>(nested);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(nested, scopeProvider.AmbientScope);
Assert.AreSame(scope, ((Scope) nested).ParentScope);
// it's moved over to call context
Assert.IsNull(httpContextItems[ScopeProvider.ScopeItemKey]);
var callContextKey = CallContext.LogicalGetData(ScopeProvider.ScopeItemKey).AsGuid();
Assert.AreNotEqual(Guid.Empty, callContextKey);
// only if Core.DEBUG_SCOPES are defined
//var ccnested = scopeProvider.CallContextObjects[callContextKey];
//Assert.AreSame(nested, ccnested);
}
// it's naturally back in http context
Assert.AreSame(scope, httpContextItems[ScopeProvider.ScopeItemKey]);
}
Assert.IsNull(scopeProvider.AmbientScope);
}
finally
{
ScopeProvider.HttpContextItemsGetter = null;
}
}
[Test]
public void NestedCreateScopeContext()
{
@@ -478,17 +528,82 @@ namespace Umbraco.Tests.Scoping
}
[Test]
public void CallContextScope()
public void CallContextScope1()
{
var scopeProvider = DatabaseContext.ScopeProvider;
var scope = scopeProvider.CreateScope();
Assert.IsNotNull(scopeProvider.AmbientScope);
using (new SafeCallContext())
using (var scope = scopeProvider.CreateScope())
{
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
using (new SafeCallContext())
{
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
using (var newScope = scopeProvider.CreateScope())
{
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientScope.ParentScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
}
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
}
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
}
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
}
[Test]
public void CallContextScope2()
{
var scopeProvider = DatabaseContext.ScopeProvider;
Assert.IsNull(scopeProvider.AmbientScope);
var httpContextItems = new Hashtable();
ScopeProvider.HttpContextItemsGetter = () => httpContextItems;
try
{
using (var scope = scopeProvider.CreateScope())
{
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
using (new SafeCallContext())
{
// pretend it's another thread
ScopeProvider.HttpContextItemsGetter = null;
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
using (var newScope = scopeProvider.CreateScope())
{
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientScope.ParentScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
}
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
// back to original thread
ScopeProvider.HttpContextItemsGetter = () => httpContextItems;
}
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
}
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
}
finally
{
ScopeProvider.HttpContextItemsGetter = null;
}
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
}
[Test]
+2 -6
View File
@@ -213,12 +213,8 @@ namespace Umbraco.Tests.Scoping
var scopeProvider = ApplicationContext.Current.ScopeProvider as IScopeProviderInternal;
Assert.IsNotNull(scopeProvider);
if (complete)
// because some event handlers trigger xml refresh with directly uses the DB
Assert.IsNotNull(scopeProvider.AmbientScope);
else
// because nothing happened
Assert.IsNull(scopeProvider.AmbientScope);
// ambient scope may be null, or maybe not, depending on whether the code that
// was called did proper scoped work, or some direct (NoScope) use of the database
Assert.IsNull(scopeProvider.AmbientContext);
// limited number of clones!
@@ -1343,6 +1343,69 @@ namespace Umbraco.Tests.Services
Assert.That(contents.Any(), Is.False);
}
[Test]
public void Can_Empty_RecycleBin_With_Content_That_Has_All_Related_Data()
{
// Arrange
//need to:
// * add relations
// * add permissions
// * add notifications
// * public access
// * tags
// * domain
// * published & preview data
// * multiple versions
var contentType = MockedContentTypes.CreateAllTypesContentType("test", "test");
ServiceContext.ContentTypeService.Save(contentType, 0);
object obj =
new
{
tags = "Hello,World"
};
var content1 = MockedContent.CreateBasicContent(contentType);
content1.PropertyValues(obj);
content1.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content1, 0);
Assert.IsTrue(ServiceContext.ContentService.PublishWithStatus(content1, 0).Success);
var content2 = MockedContent.CreateBasicContent(contentType);
content2.PropertyValues(obj);
content2.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content2, 0);
Assert.IsTrue(ServiceContext.ContentService.PublishWithStatus(content2, 0).Success);
ServiceContext.RelationService.Save(new RelationType(Constants.ObjectTypes.DocumentGuid, Constants.ObjectTypes.DocumentGuid, "test"));
Assert.IsNotNull(ServiceContext.RelationService.Relate(content1, content2, "test"));
ServiceContext.PublicAccessService.Save(new PublicAccessEntry(content1, content2, content2, new List<PublicAccessRule>
{
new PublicAccessRule
{
RuleType = "test",
RuleValue = "test"
}
}));
Assert.IsTrue(ServiceContext.PublicAccessService.AddRule(content1, "test2", "test2").Success);
Assert.IsNotNull(ServiceContext.NotificationService.CreateNotification(ServiceContext.UserService.GetUserById(0), content1, "test"));
ServiceContext.ContentService.AssignContentPermission(content1, 'A', new[] {0});
Assert.IsTrue(ServiceContext.DomainService.Save(new UmbracoDomain("www.test.com", "en-AU")
{
RootContentId = content1.Id
}).Success);
// Act
ServiceContext.ContentService.EmptyRecycleBin();
var contents = ServiceContext.ContentService.GetContentInRecycleBin();
// Assert
Assert.That(contents.Any(), Is.False);
}
[Test]
public void Can_Move_Content()
{
@@ -72,11 +72,12 @@ namespace Umbraco.Tests.TestHelpers
GetDbProviderName(),
Logger);
// fixme - bah - this is needed to reset static properties? Stephen to update this note
// ensure we start tests in a clean state ie without any scope in context
// anything that used a true 'Scope' would have removed it, but there could
// be a rogue 'NoScope' there - and we want to make sure it is gone
var scopeProvider = new ScopeProvider(null);
if (scopeProvider.AmbientScope != null)
scopeProvider.AmbientScope.Dispose();
scopeProvider.AmbientScope = null;
scopeProvider.AmbientScope.Dispose(); // removes scope from context
base.Initialize();
@@ -358,9 +358,9 @@ namespace Umbraco.Tests.TestHelpers.Entities
contentCollection.Add(new PropertyType(Constants.PropertyEditors.DateAlias, DataTypeDatabaseType.Date) { Alias = "date", Name = "Date", Mandatory = false, SortOrder = 13, DataTypeDefinitionId = -41 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.DropDownListAlias, DataTypeDatabaseType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeDefinitionId = -42 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.CheckBoxListAlias, DataTypeDatabaseType.Nvarchar) { Alias = "chklist", Name = "Checkbox List", Mandatory = false, SortOrder = 15, DataTypeDefinitionId = -43 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.ContentPickerAlias, DataTypeDatabaseType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeDefinitionId = 1034 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.MediaPickerAlias, DataTypeDatabaseType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeDefinitionId = 1035 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.MemberPickerAlias, DataTypeDatabaseType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeDefinitionId = 1036 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.ContentPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeDefinitionId = 1046 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.MediaPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeDefinitionId = 1048 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.MemberPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeDefinitionId = 1047 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.RelatedLinksAlias, DataTypeDatabaseType.Ntext) { Alias = "relatedLinks", Name = "Related Links", Mandatory = false, SortOrder = 21, DataTypeDefinitionId = 1040 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TagsAlias, DataTypeDatabaseType.Ntext) { Alias = "tags", Name = "Tags", Mandatory = false, SortOrder = 22, DataTypeDefinitionId = 1041 });
+22 -22
View File
@@ -13,41 +13,41 @@ namespace Umbraco.Tests
[Test]
public void StringEntityCtorTest()
{
var udi = new StringUdi(Constants.DeployEntityType.AnyString, "test-id");
Assert.AreEqual(Constants.DeployEntityType.AnyString, udi.EntityType);
var udi = new StringUdi(Constants.UdiEntityType.AnyString, "test-id");
Assert.AreEqual(Constants.UdiEntityType.AnyString, udi.EntityType);
Assert.AreEqual("test-id", udi.Id);
Assert.AreEqual("umb://" + Constants.DeployEntityType.AnyString + "/test-id", udi.ToString());
Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/test-id", udi.ToString());
}
[Test]
public void StringEntityParseTest()
{
var udi = Udi.Parse("umb://" + Constants.DeployEntityType.AnyString + "/test-id");
Assert.AreEqual(Constants.DeployEntityType.AnyString, udi.EntityType);
var udi = Udi.Parse("umb://" + Constants.UdiEntityType.AnyString + "/test-id");
Assert.AreEqual(Constants.UdiEntityType.AnyString, udi.EntityType);
Assert.IsInstanceOf<StringUdi>(udi);
var stringEntityId = udi as StringUdi;
Assert.IsNotNull(stringEntityId);
Assert.AreEqual("test-id", stringEntityId.Id);
Assert.AreEqual("umb://" + Constants.DeployEntityType.AnyString + "/test-id", udi.ToString());
Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/test-id", udi.ToString());
}
[Test]
public void GuidEntityCtorTest()
{
var guid = Guid.NewGuid();
var udi = new GuidUdi(Constants.DeployEntityType.AnyGuid, guid);
Assert.AreEqual(Constants.DeployEntityType.AnyGuid, udi.EntityType);
var udi = new GuidUdi(Constants.UdiEntityType.AnyGuid, guid);
Assert.AreEqual(Constants.UdiEntityType.AnyGuid, udi.EntityType);
Assert.AreEqual(guid, udi.Guid);
Assert.AreEqual("umb://" + Constants.DeployEntityType.AnyGuid + "/" + guid.ToString("N"), udi.ToString());
Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyGuid + "/" + guid.ToString("N"), udi.ToString());
}
[Test]
public void GuidEntityParseTest()
{
var guid = Guid.NewGuid();
var s = "umb://" + Constants.DeployEntityType.AnyGuid + "/" + guid.ToString("N");
var s = "umb://" + Constants.UdiEntityType.AnyGuid + "/" + guid.ToString("N");
var udi = Udi.Parse(s);
Assert.AreEqual(Constants.DeployEntityType.AnyGuid, udi.EntityType);
Assert.AreEqual(Constants.UdiEntityType.AnyGuid, udi.EntityType);
Assert.IsInstanceOf<GuidUdi>(udi);
var gudi = udi as GuidUdi;
Assert.IsNotNull(gudi);
@@ -82,9 +82,9 @@ namespace Umbraco.Tests
var guid1 = Guid.NewGuid();
var entities = new[]
{
new GuidUdi(Constants.DeployEntityType.AnyGuid, guid1),
new GuidUdi(Constants.DeployEntityType.AnyGuid, guid1),
new GuidUdi(Constants.DeployEntityType.AnyGuid, guid1),
new GuidUdi(Constants.UdiEntityType.AnyGuid, guid1),
new GuidUdi(Constants.UdiEntityType.AnyGuid, guid1),
new GuidUdi(Constants.UdiEntityType.AnyGuid, guid1),
};
Assert.AreEqual(1, entities.Distinct().Count());
}
@@ -93,12 +93,12 @@ namespace Umbraco.Tests
public void CreateTest()
{
var guid = Guid.NewGuid();
var udi = Udi.Create(Constants.DeployEntityType.AnyGuid, guid);
Assert.AreEqual(Constants.DeployEntityType.AnyGuid, udi.EntityType);
var udi = Udi.Create(Constants.UdiEntityType.AnyGuid, guid);
Assert.AreEqual(Constants.UdiEntityType.AnyGuid, udi.EntityType);
Assert.AreEqual(guid, ((GuidUdi)udi).Guid);
Assert.Throws<InvalidOperationException>(() => Udi.Create(Constants.DeployEntityType.AnyString, guid));
Assert.Throws<InvalidOperationException>(() => Udi.Create(Constants.DeployEntityType.AnyGuid, "foo"));
Assert.Throws<InvalidOperationException>(() => Udi.Create(Constants.UdiEntityType.AnyString, guid));
Assert.Throws<InvalidOperationException>(() => Udi.Create(Constants.UdiEntityType.AnyGuid, "foo"));
Assert.Throws<ArgumentException>(() => Udi.Create("barf", "foo"));
}
@@ -106,13 +106,13 @@ namespace Umbraco.Tests
public void RangeTest()
{
// can parse open string udi
var stringUdiString = "umb://" + Constants.DeployEntityType.AnyString;
var stringUdiString = "umb://" + Constants.UdiEntityType.AnyString;
Udi stringUdi;
Assert.IsTrue(Udi.TryParse(stringUdiString, out stringUdi));
Assert.AreEqual(string.Empty, ((StringUdi)stringUdi).Id);
// can parse open guid udi
var guidUdiString = "umb://" + Constants.DeployEntityType.AnyGuid;
var guidUdiString = "umb://" + Constants.UdiEntityType.AnyGuid;
Udi guidUdi;
Assert.IsTrue(Udi.TryParse(guidUdiString, out guidUdi));
Assert.AreEqual(Guid.Empty, ((GuidUdi)guidUdi).Guid);
@@ -134,12 +134,12 @@ namespace Umbraco.Tests
var guid = Guid.NewGuid();
var udi = new GuidUdi(Constants.DeployEntityType.AnyGuid, guid);
var udi = new GuidUdi(Constants.UdiEntityType.AnyGuid, guid);
var json = JsonConvert.SerializeObject(udi, settings);
Assert.AreEqual(string.Format("\"umb://any-guid/{0:N}\"", guid), json);
var dudi = JsonConvert.DeserializeObject<Udi>(json, settings);
Assert.AreEqual(Constants.DeployEntityType.AnyGuid, dudi.EntityType);
Assert.AreEqual(Constants.UdiEntityType.AnyGuid, dudi.EntityType);
Assert.AreEqual(guid, ((GuidUdi)dudi).Guid);
var range = new UdiRange(udi, Constants.DeploySelector.ChildrenOfThis);
@@ -40,27 +40,50 @@ Use this directive to generate a list of breadcrumbs.
@param {array} ancestors Array of ancestors
@param {string} entityType The content entity type (member, media, content).
@param {callback} Callback when an ancestor is clicked. It will override the default link behaviour.
**/
(function() {
'use strict';
(function () {
'use strict';
function BreadcrumbsDirective() {
function BreadcrumbsDirective() {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-breadcrumbs.html',
scope: {
ancestors: "=",
entityType: "@"
}
};
function link(scope, el, attr, ctrl) {
return directive;
scope.allowOnOpen = false;
}
scope.open = function(ancestor) {
if(scope.onOpen && scope.allowOnOpen) {
scope.onOpen({'ancestor': ancestor});
}
};
angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
function onInit() {
if ("onOpen" in attr) {
scope.allowOnOpen = true;
}
}
onInit();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-breadcrumbs.html',
scope: {
ancestors: "=",
entityType: "@",
onOpen: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
})();
@@ -21,7 +21,8 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
customtreeparams: '@',
eventhandler: '=',
enablecheckboxes: '@',
enablelistviewsearch: '@'
enablelistviewsearch: '@',
enablelistviewexpand: '@'
},
compile: function(element, attrs) {
@@ -35,7 +36,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
'<a class="umb-options" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(tree.root, $event)" ng-swipe-right="options(tree.root, $event)"><i></i><i></i><i></i></a>' +
'</div>';
template += '<ul>' +
'<umb-tree-item ng-repeat="child in tree.root.children" eventhandler="eventhandler" node="child" current-node="currentNode" tree="this" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
'<umb-tree-item ng-repeat="child in tree.root.children" enablelistviewexpand="{{enablelistviewexpand}}" eventhandler="eventhandler" node="child" current-node="currentNode" tree="this" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
'</ul>' +
'</li>' +
'</ul>';
@@ -27,6 +27,7 @@ angular.module("umbraco.directives")
section: '@',
eventhandler: '=',
currentNode: '=',
enablelistviewexpand: '@',
node: '=',
tree: '='
},
@@ -38,7 +39,7 @@ angular.module("umbraco.directives")
'<div ng-class="getNodeCssClass(node)" ng-swipe-right="options(node, $event)" >' +
//NOTE: This ins element is used to display the search icon if the node is a container/listview and the tree is currently in dialog
//'<ins ng-if="tree.enablelistviewsearch && node.metaData.isContainer" class="umb-tree-node-search icon-search" ng-click="searchNode(node, $event)" alt="searchAltText"></ins>' +
'<ins ng-class="{\'icon-navigation-right\': !node.expanded, \'icon-navigation-down\': node.expanded}" ng-click="load(node)">&nbsp;</ins>' +
'<ins ng-class="{\'icon-navigation-right\': !node.expanded || node.metaData.isContainer, \'icon-navigation-down\': node.expanded && !node.metaData.isContainer}" ng-click="load(node)">&nbsp;</ins>' +
'<i class="icon umb-tree-icon sprTree" ng-click="select(node, $event)"></i>' +
'<a href="#/{{node.routePath}}" ng-click="select(node, $event)"></a>' +
//NOTE: These are the 'option' elipses
@@ -74,11 +75,12 @@ angular.module("umbraco.directives")
//toggle visibility of last 'ins' depending on children
//visibility still ensure the space is "reserved", so both nodes with and without children are aligned.
if (!node.hasChildren) {
element.find("ins").last().css("visibility", "hidden");
if (node.hasChildren || node.metaData.isContainer && scope.enablelistviewexpand === "true") {
element.find("ins").last().css("visibility", "visible");
}
else {
element.find("ins").last().css("visibility", "visible");
element.find("ins").last().css("visibility", "hidden");
}
var icon = element.find("i:first");
@@ -192,7 +194,7 @@ angular.module("umbraco.directives")
emits treeNodeCollapsing event if already expanded and treeNodeExpanding if collapsed
*/
scope.load = function (node) {
if (node.expanded) {
if (node.expanded && !node.metaData.isContainer) {
deleteAnimations = false;
emitEvent("treeNodeCollapsing", { tree: scope.tree, node: node, element: element });
node.expanded = false;
@@ -227,7 +229,7 @@ angular.module("umbraco.directives")
setupNodeDom(scope.node, scope.tree);
var template = '<ul ng-class="{collapsed: !node.expanded}"><umb-tree-item ng-repeat="child in node.children" eventhandler="eventhandler" tree="tree" current-node="currentNode" node="child" section="{{section}}" ng-animate="animation()"></umb-tree-item></ul>';
var template = '<ul ng-class="{collapsed: !node.expanded}"><umb-tree-item ng-repeat="child in node.children" enablelistviewexpand="{{enablelistviewexpand}}" eventhandler="eventhandler" tree="tree" current-node="currentNode" node="child" section="{{section}}" ng-animate="animation()"></umb-tree-item></ul>';
var newElement = angular.element(template);
$compile(newElement)(scope);
element.append(newElement);
@@ -0,0 +1,209 @@
(function () {
'use strict';
function MiniListViewDirective(contentResource, memberResource, mediaResource) {
function link(scope, el, attr, ctrl) {
scope.search = "";
scope.miniListViews = [];
scope.breadcrumb = [];
var miniListViewsHistory = [];
var goingForward = true;
var skipAnimation = true;
function onInit() {
open(scope.node);
}
function open(node) {
goingForward = true;
var miniListView = {
node: node,
loading: true,
pagination: {
pageSize: 10,
pageNumber: 1,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder",
orderBySystemField: true
}
};
// clear and push mini list view in dom so we only render 1 view
scope.miniListViews = [];
scope.miniListViews.push(miniListView);
// store in history so we quickly can navigate back
miniListViewsHistory.push(miniListView);
// get children
getChildrenForMiniListView(miniListView);
makeBreadcrumb();
}
function getChildrenForMiniListView(miniListView) {
// start loading animation list view
miniListView.loading = true;
// setup the correct resource depending on section
var resource = "";
if (scope.entityType === "Member") {
resource = memberResource.getPagedResults;
} else if (scope.entityType === "Media") {
resource = mediaResource.getChildren;
} else {
resource = contentResource.getChildren;
}
resource(miniListView.node.id, miniListView.pagination)
.then(function (data) {
// update children
miniListView.children = data.items;
// update pagination
miniListView.pagination.totalItems = data.totalItems;
miniListView.pagination.totalPages = data.totalPages;
// stop load indicator
miniListView.loading = false;
});
}
scope.openNode = function(event, node) {
open(node);
event.stopPropagation();
};
scope.selectNode = function(node) {
if(scope.onSelect) {
scope.onSelect({'node': node});
}
};
/* Pagination */
scope.goToPage = function(pageNumber, miniListView) {
// set new page number
miniListView.pagination.pageNumber = pageNumber;
// get children
getChildrenForMiniListView(miniListView);
};
/* Breadcrumb */
scope.clickBreadcrumb = function(ancestor) {
var found = false;
goingForward = false;
angular.forEach(miniListViewsHistory, function(historyItem, index){
// We need to make sure we can compare the two id's.
// Some id's are integers and others are strings.
// Members have string ids like "all-members".
if(historyItem.node.id.toString() === ancestor.id.toString()) {
// load the list view from history
scope.miniListViews = [];
scope.miniListViews.push(historyItem);
// clean up history - remove all children after
miniListViewsHistory.splice(index + 1, miniListViewsHistory.length);
found = true;
}
});
if(!found) {
// if we can't find the view in the history - close the list view
scope.exitMiniListView();
}
// update the breadcrumb
makeBreadcrumb();
};
scope.showBackButton = function() {
// don't show the back button if the start node is a list view
if(scope.node.metaData && scope.node.metaData.IsContainer || scope.node.isContainer) {
return false;
} else {
return true;
}
};
scope.exitMiniListView = function() {
miniListViewsHistory = [];
scope.miniListViews = [];
if(scope.onClose) {
scope.onClose();
}
};
function makeBreadcrumb() {
scope.breadcrumb = [];
angular.forEach(miniListViewsHistory, function(historyItem){
scope.breadcrumb.push(historyItem.node);
});
}
/* Search */
scope.searchMiniListView = function(search, miniListView) {
// set search value
miniListView.pagination.filter = search;
// reset pagination
miniListView.pagination.pageNumber = 1;
// start loading animation list view
miniListView.loading = true;
searchMiniListView(miniListView);
};
var searchMiniListView = _.debounce(function (miniListView) {
scope.$apply(function () {
getChildrenForMiniListView(miniListView);
});
}, 500);
/* Animation */
scope.getMiniListViewAnimation = function() {
// disable the first "slide-in-animation"" if the start node is a list view
if(scope.node.metaData && scope.node.metaData.IsContainer && skipAnimation || scope.node.isContainer && skipAnimation) {
skipAnimation = false;
return;
}
if(goingForward) {
return 'umb-mini-list-view--forward';
} else {
return 'umb-mini-list-view--backwards';
}
};
onInit();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-mini-list-view.html',
scope: {
node: "=",
entityType: "@",
startNodeId: "=",
onSelect: "&",
onClose: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbMiniListView', MiniListViewDirective);
})();
@@ -134,25 +134,40 @@ Use this directive to generate a pagination.
}
scope.next = function() {
if (scope.onNext && scope.pageNumber < scope.totalPages) {
scope.pageNumber++;
scope.onNext(scope.pageNumber);
}
scope.next = function () {
if (scope.pageNumber < scope.totalPages) {
scope.pageNumber++;
if (scope.onNext) {
scope.onNext(scope.pageNumber);
}
if (scope.onChange) {
scope.onChange({ "pageNumber": scope.pageNumber });
}
}
};
scope.prev = function(pageNumber) {
if (scope.onPrev && scope.pageNumber > 1) {
scope.pageNumber--;
scope.onPrev(scope.pageNumber);
}
scope.prev = function (pageNumber) {
if (scope.pageNumber > 1) {
scope.pageNumber--;
if (scope.onPrev) {
scope.onPrev(scope.pageNumber);
}
if (scope.onChange) {
scope.onChange({ "pageNumber": scope.pageNumber });
}
}
};
scope.goToPage = function(pageNumber) {
if(scope.onGoToPage) {
scope.pageNumber = pageNumber + 1;
scope.onGoToPage(scope.pageNumber);
}
scope.goToPage = function (pageNumber) {
scope.pageNumber = pageNumber + 1;
if (scope.onGoToPage) {
scope.onGoToPage(scope.pageNumber);
}
if (scope.onChange) {
if (scope.onChange) {
scope.onChange({ "pageNumber": scope.pageNumber });
}
}
};
var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){
@@ -176,7 +191,8 @@ Use this directive to generate a pagination.
totalPages: "=",
onNext: "=",
onPrev: "=",
onGoToPage: "="
onGoToPage: "=",
onChange: "&"
},
link: link
};
@@ -205,6 +205,38 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
"codeFileApiBaseUrl",
"GetScaffold?type=" + type + "&id=" + id + "&snippetName=" + snippetName)),
"Failed to get scaffold for" + type);
},
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#createContainer
* @methodOf umbraco.resources.codefileResource
*
* @description
* Creates a container/folder
*
* ##usage
* <pre>
* codefileResource.createContainer("partialViews", "folder%2ffolder", "folder")
* .then(function(data) {
* alert('its here!');
* });
* </pre>
*
* @param {string} File type: (scripts, partialViews, partialViewMacros).
* @param {string} Parent Id: url encoded path
* @param {string} Container name
* @returns {Promise} resourcePromise object.
*
*/
createContainer: function(type, parentId, name) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"PostCreateContainer",
{ type: type, parentId: parentId, name: encodeURIComponent(name) })),
'Failed to create a folder under parent id ' + parentId);
}
};
@@ -166,24 +166,17 @@ function entityResource($q, $http, umbRequestHelper) {
*/
getByIds: function (ids, type) {
var query = "";
_.each(ids, function(item) {
query += "ids=" + item + "&";
});
// if ids array is empty we need a empty variable in the querystring otherwise the service returns a error
if (ids.length === 0) {
query += "ids=&";
}
query += "type=" + type;
var query = "type=" + type;
return umbRequestHelper.resourcePromise(
$http.get(
$http.post(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetByIds",
query)),
query),
{
ids: ids
}),
'Failed to retrieve entity data for ids ' + ids);
},
@@ -118,6 +118,8 @@
@import "components/umb-avatar.less";
@import "components/umb-progress-bar.less";
@import "components/umb-querybuilder.less";
@import "components/umb-pagination.less";
@import "components/umb-mini-list-view.less";
@import "components/buttons/umb-button.less";
@import "components/buttons/umb-button-group.less";
@@ -0,0 +1,97 @@
.umb-mini-list-view__title {
display: flex;
align-items: center;
}
.umb-mini-list-view__title-text {
font-size: 16px;
font-weight: bold;
}
.umb-mini-list-view__title-icon {
font-size: 20px;
margin-right: 5px;
}
.umb-mini-list-view__back {
font-size: 11px;
margin-right: 5px;
color: @gray;
display: flex;
align-items: center;
}
.umb-mini-list-view__back-icon {
margin-right: 4px;
height: 11px;
line-height: 11px;
}
.umb-mini-list-view__back-text {
text-decoration: underline;
margin-right: 5px;
}
.umb-mini-list-view__back:hover {
opacity: 1;
text-decoration: none;
color: @black;
}
/* Animations */
/* Forward */
.umb-mini-list-view--forward-enter,
.umb-mini-list-view--forward-leave
{
transition: 120ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
position: relative;
display: block;
}
.umb-mini-list-view--forward-enter {
left: 100%;
}
.umb-mini-list-view--forward-enter.umb-mini-list-view--forward-enter-active {
left: 0;
opacity: 1;
}
.umb-mini-list-view--forward-leave {
left: 0;
}
.umb-mini-list-view--forward-leave.umb-mini-list-view--forward-leave-active{
left: -100%;
opacity: 0;
}
/* Backwards */
.umb-mini-list-view--backwards-enter,
.umb-mini-list-view--backwards-leave
{
transition: 120ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
position: relative;
display: block;
}
.umb-mini-list-view--backwards-enter {
right: 100%;
}
.umb-mini-list-view--backwards-enter.umb-mini-list-view--backwards-enter-active {
right: 0;
opacity: 1;
}
.umb-mini-list-view--backwards-leave {
left: 0;
}
.umb-mini-list-view--backwards-leave.umb-mini-list-view--backwards-leave-active{
right: -100%;
opacity: 0;
}
@@ -0,0 +1,3 @@
.umb-pagination ul {
box-shadow: none;
}
@@ -10,7 +10,7 @@
flex-wrap: nowrap;
justify-content: space-between;
min-width: 640px;
min-width: auto;
}
.umb-table.umb-table-inactive {
@@ -103,11 +103,17 @@ input.umb-table__input {
// Table Body Styles
.umb-table-body {
position: relative;
}
.umb-table-body .umb-table-row {
color: fade(@gray, 75%);
border-top: 1px solid @grayLight;
cursor: pointer;
font-size: 13px;
position: relative;
min-height: 32px;
&:hover {
background-color: fade(@grayLighter, 90%);
@@ -198,30 +204,33 @@ input.umb-table__input {
}
}
// Table Row Styles
.umb-table-row {
display: flex;
flex-flow: row nowrap;
align-items: center;
user-select: none;
}
.umb-table-body .umb-table-row--empty {
flex: 1 1 auto;
display: flex;
justify-content: center;
padding: 5px 0;
cursor: auto;
user-select: auto;
}
.umb-table-body .umb-table-row--empty:hover {
background-color: transparent;
cursor: auto;
}
.umb-table-row.-selected,
.umb-table-row.-selected:hover {
background-color: fade(@blueDark, 4%);
}
// Table Cell Styles
.umb-table-cell {
display: flex;
@@ -250,9 +259,44 @@ input.umb-table__input {
padding: 15px 0;
}
.umb-table-cell--auto-width {
flex: 0 0 auto !important;
}
.umb-table-cell--faded {
opacity: 0.4;
}
// Increases the space for the name cell
.umb-table__name {
flex: 1 1 25%;
max-width: 25%;
}
.umb-table__loading-overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.7);
z-index: 1;
}
.umb-table__row-expand {
font-size: 12px;
text-decoration: none;
color: @black;
}
.umb-table--condensed {
.umb-table-cell:first-of-type:not(.not-fixed) {
padding-top: 10px;
padding-bottom: 10px;
}
.umb-table-body__icon {
font-size: 20px;
}
}
@@ -156,6 +156,11 @@ h5.-black {
margin-left: 0;
}
/* CONTROL VALIDATION */
.umb-control-required {
color: @controlRequiredColor;
}
.controls-row {
padding-bottom: 5px;
margin-left: 240px;
@@ -36,6 +36,7 @@
.red{color: @red;}
.blue{color: @blue;}
.black{color: @black;}
//icon colors for tree icons
@@ -136,6 +137,7 @@
@inputDisabledBackground: @grayLighter;
@formActionsBackground: #f5f5f5;
@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border
@controlRequiredColor: #ee5f5b;
// Tabs
@@ -351,4 +353,4 @@
// SORTABLE
// --------------------------------------------------
@sortableHelperBg: rgba(4, 156, 219, 0.5);
@sortablePlaceholderBg : @blue;
@sortablePlaceholderBg : @blue;
@@ -1,34 +1,48 @@
<div ng-controller="Umbraco.Overlays.TreePickerController" ng-init="init('content')">
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="hideSearch"
search-callback="onSearchResults"
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
section="content">
</umb-tree-search-box>
</div>
<div ng-hide="miniListView">
<umb-tree-search-results
ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="hideSearch"
search-callback="onSearchResults"
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
section="content">
</umb-tree-search-box>
</div>
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree
section="content"
treealias="content"
hideheader="{{hideHeader}}"
hideoptions="true"
isdialog="true"
customtreeparams="{{customTreeParams}}"
eventhandler="dialogTreeEventHandler"
enablelistviewsearch="true"
enablecheckboxes="{{multiPicker}}">
</umb-tree>
</div>
<umb-tree-search-results
ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
</div>
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree
section="content"
treealias="content"
hideheader="{{hideHeader}}"
hideoptions="true"
isdialog="true"
customtreeparams="{{customTreeParams}}"
eventhandler="dialogTreeEventHandler"
enablelistviewsearch="true"
enablelistviewexpand="true"
enablecheckboxes="{{multiPicker}}">
</umb-tree>
</div>
</div>
<umb-mini-list-view
ng-if="miniListView"
node="miniListView"
entity-type="{{entityType}}"
start-node-id="model.startNodeId"
on-select="selectListViewNode(node)"
on-close="closeMiniListView()">
</umb-mini-list-view>
</div>
@@ -1,34 +1,47 @@
<div ng-controller="Umbraco.Overlays.TreePickerController" ng-init="init('member')">
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="hideSearch"
search-callback="onSearchResults"
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
section="member">
</umb-tree-search-box>
</div>
<div ng-hide="miniListView">
<umb-tree-search-results
ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="hideSearch"
search-callback="onSearchResults"
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
section="member">
</umb-tree-search-box>
</div>
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree
section="member"
treealias="member"
hideheader="{{hideHeader}}"
hideoptions="true"
isdialog="true"
customtreeparams="{{customTreeParams}}"
eventhandler="dialogTreeEventHandler"
enablelistviewsearch="true"
enablecheckboxes="{{multiPicker}}">
</umb-tree>
</div>
<umb-tree-search-results
ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree
section="member"
treealias="member"
hideheader="{{hideHeader}}"
hideoptions="true"
isdialog="true"
customtreeparams="{{customTreeParams}}"
eventhandler="dialogTreeEventHandler"
enablelistviewsearch="true"
enablelistviewexpand="true"
enablecheckboxes="{{multiPicker}}">
</umb-tree>
</div>
</div>
<umb-mini-list-view
ng-if="miniListView"
node="miniListView"
entity-type="{{entityType}}"
start-node-id="model.startNodeId"
on-select="selectListViewNode(node)"
on-close="closeMiniListView()">
</umb-mini-list-view>
</div>

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