Compare commits

...

427 Commits

Author SHA1 Message Date
Shannon c0c7d21001 Merge remote-tracking branch 'origin/6.2.3' into 7.1.7 2014-10-03 00:29:33 +10:00
Shannon 7d189a6fb8 Backports the @ symbol escaping fixes 2014-10-03 00:28:49 +10:00
Shannon e60dcfa144 Merge branch '7.1.7' of https://github.com/umbraco/Umbraco-CMS into 7.1.7 2014-10-03 00:26:25 +10:00
Shannon 3f8b779ae4 adds note 2014-10-03 00:23:38 +10:00
Shannon 72dc5fc477 Removes unused method 2014-10-03 00:23:28 +10:00
Shannon 737f2fb707 Fixes: U4-5566 Quite a few hard coded queries with incorrect escape syntax for mysql 2014-10-03 00:07:40 +10:00
Shannon a13ead0d10 unescapes @ symbols now that sql params are in place 2014-10-03 00:06:09 +10:00
Shannon 414dbe45a6 Fixes: U4-5460 User with start content set won't see the selected node. 2014-10-02 13:38:44 +02:00
Sebastiaan Janssen 340328aeba Fixes merge issue 2014-10-02 11:44:41 +02:00
Shannon f875dc15f7 Merge branch '6.2.3' into 7.1.7
Conflicts:
	build/Build.bat
	build/NuSpecs/UmbracoCms.Core.nuspec
	build/NuSpecs/UmbracoCms.nuspec
	build/NuSpecs/build/UmbracoCms.targets
	src/Umbraco.Core/Configuration/UmbracoVersion.cs
	src/Umbraco.Core/Models/DeepCloneHelper.cs
	src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
	src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs
	src/Umbraco.Core/Services/MediaService.cs
	src/Umbraco.Core/Umbraco.Core.csproj
	src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs
	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
	src/Umbraco.Web.UI/umbraco/dashboard/DesktopMediaUploader.ascx
	src/Umbraco.Web/Umbraco.Web.csproj
	src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/DesktopMediaUploader.ascx
2014-10-01 16:47:49 +10:00
Shannon 71242430da Fixes a query that would cause the new petapoco changes to have issues since it was asking for the wrong type 2014-10-01 16:37:42 +10:00
Shannon 1e04735826 updates iis port 2014-09-29 16:47:00 +02:00
Shannon b975a02bfa Updates version to 7.1.7 2014-09-29 16:46:53 +02:00
Shannon 88ec514630 fixes entity repo merges, this also means that the n+1 fixes for entity repository will be in this release. 2014-09-29 16:46:46 +02:00
Shannon 6f7ec84f18 Backports fixes to SQL parameterization and PetaPoco mem updates 2014-09-29 16:46:37 +02:00
Shannon 4ee4f296fc updates version 2014-09-29 17:31:24 +10:00
Shannon b9e4950e36 fixes up entity repo merges to support old db schema 2014-09-29 17:30:09 +10:00
Shannon ba52f9235e Backports fixes to SQL parameterization and PetaPoco mem updates 2014-09-29 17:11:58 +10:00
Sebastiaan Janssen 33ccf490ab Update nuspec to ask for a valid nuget pkg 2014-08-25 15:53:45 +02:00
Sebastiaan Janssen 101e116baf Bump version number 2014-08-25 15:14:21 +02:00
Shannon ebf24d69b5 Fixes: U4-5384 Custom ApiController routes not working after upgrade to 7.1.5 2014-08-25 14:33:50 +02:00
Shannon c94662e49c Fixes: U4-5388 YSOD after doctype rename & U4-5387 Deleting Properties in document types causes "Object reference not set to an instance of an object" 2014-08-25 14:33:03 +02:00
Sebastiaan Janssen 5d37eaf993 #U4-4673 Fixed Due in version: 7.1.6 2014-08-25 14:32:35 +02:00
Sebastiaan Janssen 1ded2c9baf #U4-5385 fixed Due in version: 7.1.6 2014-08-25 14:32:14 +02:00
Sebastiaan Janssen 2daacd8d57 #U4-5379 Fixed Due in version: 7.1.6
Some users have not set an email, don't strip out empty entries
2014-08-25 14:31:50 +02:00
Shannon 3f4fa8b8ed Fixes: U4-5380 Booting.aspx security issue 2014-08-25 14:31:04 +02:00
Sebastiaan Janssen 03ba8c9e14 Update to 6.2.2, removes airinstallbadge 2014-08-25 13:46:21 +02:00
Shannon 13b57c7638 adds some logging 2014-08-25 16:40:45 +10:00
Shannon a8b297903d Ensures we don't get any ysods when changing doc type properties and the content re-creation since it's running on an async thread (U4-5388 , U4-5387 ) 2014-08-25 16:16:42 +10:00
Shannon 89b5774106 Fixes: U4-5380 Booting.aspx security issue 2014-08-21 14:39:06 -06:00
Sebastiaan Janssen 3a4d0a02eb Fix redirect issue 2014-08-21 14:16:58 +02:00
Sebastiaan Janssen 2666742851 The admin (0) user in UaaS always has password "default" (unencrypted), checking for that for
now until we can fix this properly. If we didn't update this scheduled publishing would be broken
2014-08-21 14:10:54 +02:00
Sebastiaan Janssen 38c1e154d4 Fixes broken relation type manager 2014-08-21 12:08:43 +02:00
Shannon 774f201514 Adds a simple cache mechanism to hold reflected property info when cloning - reduces perf to 10% of what it used to be 2014-08-20 20:09:05 -06:00
Shannon 067eceffce adds a couple of benchmark tests if we need to refer to them in the future (they are ignored so won't run as part of the normal build) 2014-08-20 19:36:03 -06:00
Shannon 573c08295a Adds a simple cache mechanism to hold reflected property info when cloning - reduces perf to 10% of what it used to be 2014-08-20 19:27:53 -06:00
Shannon fc558198d2 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-08-20 18:04:11 -06:00
Shannon de1b69d5ae adds some notes 2014-08-20 18:04:05 -06:00
Shannon Deminick 03358ca1ca Merge pull request #454 from leekelleher/U4-5005
U4-5005 - YesNoValueConverter fails for int
2014-08-20 13:10:57 -06:00
Shannon 81d8a90f59 Adds some simple request based caching for the retrieval of the current member in the Umbraco helper. 2014-08-20 12:31:54 -06:00
Shannon 6a28f246f6 Removes the examine 'Azure' proj's from the sln, we don't currently ship these and the azuredirectory proj has been unlisted from nuget 2014-08-20 11:08:11 -06:00
Shannon 15a1887ccb Merge remote-tracking branch 'origin/6.2.2' into 7.1.5 2014-08-20 10:30:51 -06:00
Sebastiaan Janssen 2d5e5e9b3c Refactored initial relationtype creation as well and "Relate on copy" is now also using the relationservice so it fires events 2014-08-20 17:01:12 +02:00
Sebastiaan Janssen 03a845c957 Update relation type editors to use new RelationService so we can subscribe to it's events 2014-08-20 15:58:57 +02:00
Sebastiaan Janssen b0d03e7493 Fixed #U4-5373
Also cleaned up code a little
2014-08-20 15:16:39 +02:00
Sebastiaan Janssen ee2410ebe2 Fixed #U4-4287 Due in version 7.1.5
Added the locale name after each language so you know what exactly you're picking
2014-08-20 14:50:31 +02:00
Morten Christensen 7d52ed873e Merge pull request #449 from nul800sebastiaan/uaas-events
Adding events for use by Courier/UaaS
2014-08-20 14:12:38 +02:00
Sebastiaan Janssen de34379ddf Updating PR after feedback from @sitereactor 2014-08-20 14:08:45 +02:00
leekelleher b176d7dca0 Amends CreateTable, so if 'overwrite' is enabled, the table is dropped, then recreated. 2014-08-20 10:46:37 +02:00
Sebastiaan Janssen 12e2aa679b Merge pull request #455 from leekelleher/U4-4249
U4-4249 - PetaPocoExtensions CreateTable doesn't recreate the table
2014-08-20 10:45:56 +02:00
Sebastiaan Janssen 873eac7f24 Updating json.net dependency to 6.0.4 2014-08-20 10:44:05 +02:00
leekelleher 1978930fa2 Amends CreateTable, so if 'overwrite' is enabled, the table is dropped, then recreated. 2014-08-19 18:56:15 +01:00
leekelleher d2ee281bfa Refactored the YesNoValueConverter to evaluate non-string objects.
Added supporting unit-tests.
2014-08-19 18:40:03 +01:00
Sebastiaan Janssen 468cdf64ff Merge pull request #447 from Jeavon/UpdateImageProcessor
Update ImageProcessor to v1.9.0 and ImageProcessor.Web to v3.3.0
2014-08-19 14:37:34 +02:00
Sebastiaan Janssen 5752bb0d18 Merge pull request #412 from BatJan/7.1.5-ux-4913
Fix for U4-4913
2014-08-19 14:20:47 +02:00
Sebastiaan Janssen a0f5a717f2 Merge pull request #410 from BatJan/7.1.5-installer-fix-4493
Fix for U4-4493
2014-08-19 14:18:10 +02:00
Sebastiaan Janssen 8b34b4e8a1 Merge pull request #416 from Graphicnerd/7.1.5
Updated the sortable frame in Umbraco Backoffice
2014-08-19 14:02:28 +02:00
Sebastiaan Janssen 6152079af1 Merge pull request #450 from bjarnef/7.1.5
Fix for U4-5313 and U4-5330
2014-08-19 13:44:02 +02:00
Sebastiaan Janssen e638cbf4e6 Merge pull request #444 from netmester/7.1.5-packaging-bugfix
7.1.5 packaging bugfix
2014-08-19 13:16:55 +02:00
Sebastiaan Janssen a9277113b1 Added TODO's for when we want to make some methods public 2014-08-19 12:59:25 +02:00
Sebastiaan Janssen 1805857eea Refactor PartialView.IsValid with logic found in SaveFileController
Refactor FileService.CreatePartialView to use Attempt so that it gets rid of PartialView.SaveSucceeded
Refactor SaveFileController to move all saving logic into the FileService
Added SavingPartialView and SavedPartialView events to the FileService
2014-08-19 12:51:07 +02:00
Sebastiaan Janssen 32057bd7ee Macro instead of Macr. Removed events for SaveTemplate as the old Template class already has events 2014-08-19 10:39:52 +02:00
Sebastiaan Janssen 747ce1c013 Making sure memorystream is disposed 2014-08-19 10:21:01 +02:00
per ploug 3a0a839503 Increasing the search debounce from 100 to 300ms 2014-08-19 00:04:53 +02:00
Shannon f4c2b7419a Fixes: U4-4782 Duplicate content after validation error correction 2014-08-18 15:02:45 -06:00
Shannon bbe8c8e8e6 moves no-dirty-check directive to validation folder 2014-08-18 14:49:41 -06:00
Shannon 49d1171a99 renames utill folder to have a single l 2014-08-18 14:48:36 -06:00
Shannon b44699ff4a typo on docs 2014-08-18 11:55:31 -06:00
Shannon 086d81438c Merge branch 'AndyButland-u4-5003' into 7.1.5 2014-08-18 11:40:50 -06:00
Shannon 895ecf5f16 Merge branch 'u4-5003' of https://github.com/AndyButland/Umbraco-CMS into AndyButland-u4-5003 2014-08-18 11:38:09 -06:00
Shannon Deminick 9d95e9f4d3 Merge pull request #452 from kgiszewski/feature/rte-config-cache
Add caching to the RTE config
2014-08-18 10:34:07 -06:00
Sebastiaan Janssen bba0a58bac AssignedApp property is of no concern to FileService 2014-08-18 18:08:01 +02:00
Sebastiaan Janssen 14cbef5edf Add some logging to the cancelled event 2014-08-18 18:02:35 +02:00
Sebastiaan Janssen 65a2b79189 Moves PartialView save/delete to the FileService 2014-08-18 17:47:55 +02:00
Kevin Giszewski 42dd38fb29 Add caching to the RTE config 2014-08-18 09:23:46 -04:00
Shannon Deminick 023a8da5ba Updated DetectIsEmptyJson with more robust sln 2014-08-15 14:23:21 -06:00
Shannon 4dc559a8d1 Removes class constraint on RenderModel<T> and UmbracoTemplatePage<T> which will make things more flexible. 2014-08-15 14:14:05 -06:00
Shannon 1686e2f4bf Fixes: U4-5333 Datepicker starts on today's date instead of current value when you switch nodes 2014-08-15 13:48:07 -06:00
Shannon 69244f6991 Fixes: U4-4263 U7 - HasValue method returns true for Json editors when there is no value 2014-08-15 13:21:00 -06:00
Shannon 4fe029a695 Fixes: U4-5291 UmbracoVirtualNodeRouteHandler throws exception: parameter culture cannot be null and publicizes some routing extensions. 2014-08-15 12:47:50 -06:00
Shannon 0fd37d708d Merge branch 'U4-5332' of https://github.com/bjarnef/Umbraco-CMS into bjarnef-U4-5332 2014-08-15 12:04:54 -06:00
Shannon f6abb0f603 fixes more of the photo arrangements in the photo folder which also fixes unit tests 2014-08-15 11:58:41 -06:00
Shannon 9bb8ee5f95 ensure even when using the legacy api that the icon for media folder is correct 2014-08-15 11:57:01 -06:00
Shannon ac55724231 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-08-15 09:34:47 -06:00
Morten Christensen 5789ac28d2 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-08-15 14:45:54 +02:00
Morten Christensen bc6ef58235 Making a few consistency updates to the TagService 2014-08-15 14:45:26 +02:00
Morten Christensen 9236225bdf Adding and updating code comments for the Membership service interfaces and implementations 2014-08-15 14:42:05 +02:00
Morten Christensen 6dbc30e094 Correcting a few comments on the UserService 2014-08-15 14:41:25 +02:00
Morten Christensen 1d4d8066ed Adds and updates code comments on the UserService 2014-08-15 11:13:00 +02:00
Morten Christensen 7a39751101 Adds code comments to the TagService and surrounding classes 2014-08-15 10:30:12 +02:00
Stephan c4e4f9579b U4-4931 - fix tests 2014-08-15 09:54:04 +02:00
Stephan 43ae301683 U4-4931 - fix tests 2014-08-15 09:50:55 +02:00
bjarnef d978db588c Fix for U4-5313 and U4-5330
Make the bottom bar overlap the image cropper focal point and display
the scrollbar if there is overflow. I know the buttons are located a bit
(10px) closer to the top, but you can see the horizontal scrollbar then,
when there is one in e.g. a package. E.g. depending on the screen
resolution, I would in Matrix Property Editor (There is no spoon) prefer
the Umbracos editing section to scroll than add scroll inside the
property editor wrapper, because it give more editing space.
2014-08-15 01:06:20 +02:00
Stephan 9782f66507 U4-5084 - fix commit 2014-08-14 22:24:12 +02:00
Stephan 16f8db9dea U4-5084 - fix commit 2014-08-14 22:21:24 +02:00
Stephan e9938afc83 Merge pull request #418 from zpqrtbnk/6.2-4931
U4-4931 - lock contention & potential application lockdown
2014-08-14 20:52:13 +02:00
Stephan fcda9821d0 Merge branch '6.2.2' into 6.2-4931 2014-08-14 20:50:37 +02:00
Stephan bdc234b18a U4-5084 - DynamicPublishedContentList ext methods confusion 2014-08-14 20:49:49 +02:00
Stephan 648cf69d45 U4-5084 - DynamicPublishedContentList ext methods confusion 2014-08-14 20:37:04 +02:00
Stephan a0fcd2c7f9 Merge pull request #423 from zpqrtbnk/7.1-4931
U4-4931 - lock contention & potential application lockdown
2014-08-14 20:28:18 +02:00
Stephan d603c08e1f Merge branch '7.1.5' into 7.1-4931 2014-08-14 20:22:56 +02:00
AndyButland 53097a9b84 Moved added method from PetaPoco file to an extension method 2014-08-14 18:16:49 +02:00
AndyButland beda0968ae Removed debug line 2014-08-14 18:07:21 +02:00
Sebastiaan Janssen 1ccee3f305 Moved all logic of PartialViewTasks and PartialViewMacroTasks to a base class as the only difference was 3 variables
Added events: PartialView(Macro): Create, Save and Delete
Added events: Template: Save (there's already a legacy event, this is a new one to transition to)
PartialViewMacro.ascx.cs was adding ".cshtml" just so that it could strip it of again, crazy
2014-08-14 17:20:25 +02:00
Sebastiaan Janssen 908afbd264 #U4-2238 Fixed
Issue when saving/publishing special characters - '' hexadecimal value 0x03, is an invalid character
2014-08-14 10:26:38 +02:00
Sebastiaan Janssen c548ad6a98 Update failing unit test now that MaxInvalidPasswordAttempts is 20 by default 2014-08-14 10:16:02 +02:00
AndyButland ca2dd3eb47 Fixed issue with list view filter; further unit tests for list view paging 2014-08-14 08:42:36 +02:00
AndyButland 8ea5b6fb46 Unit tests for content repository paged queries; amends to fix ordering issues 2014-08-14 00:16:37 +02:00
AndyButland f6ec4798a6 Prevented list view filter box from setting dirty flag on form. Without this you get the save changes warning after using the filter, but there's nothing to save. 2014-08-13 22:11:41 +01:00
AndyButland 096e10aad4 Moved list view no results message into table body as otherwise if you apply a filter that returns no results, you can't clear it again 2014-08-13 20:06:55 +01:00
AndyButland 817d5aab0d Moved list view filter to repository, so can be applied with a database query 2014-08-13 20:02:51 +01:00
Shannon Deminick 0aff4b4d05 Merge pull request #446 from nul800sebastiaan/U4-2238-hexadecimal
U4-2238 6.0.5 - Issue when saving/publishing special characters - '' hexadecimal value 0x03, is an invalid character
2014-08-13 09:57:30 -06:00
per ploug 5e738a2b7b Fixed: U4-4734 Remove default alt text on images from RTE 2014-08-13 11:01:56 +02:00
per ploug 652b9d1a86 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-08-13 10:42:54 +02:00
per ploug dd72fd04bd Fixed: U4-5025 Backoffice spelling errors 2014-08-13 10:42:46 +02:00
Sebastiaan Janssen 83668db6e3 Update failing unit test now that MaxInvalidPasswordAttempts is 20 by default 2014-08-13 09:44:25 +02:00
Sebastiaan Janssen ba7a5a0e8a #U4-2238 Fixed
Moved some methods around, made them internal, removed cleaning of tags as that's already done by cleaning each property
2014-08-13 09:38:40 +02:00
AndyButland a71bfaa14c Amends to generated SQL pre-paging to ensure only the latest content versions are returned 2014-08-12 23:32:13 +01:00
Jeavon Leopold 6b455aaa19 Add ImageProcessor httpmodule to web.template.config 2014-08-12 17:51:30 +01:00
Jeavon Leopold b2ca814fdb Update ImageProcessor to v1.9.0 and ImageProcessor.Web to v3.3.0 2014-08-12 16:59:31 +01:00
Sebastiaan Janssen 9523e5c854 U4-2238 6.0.5 - Issue when saving/publishing special characters - '' hexadecimal value 0x03, is an invalid character 2014-08-12 14:18:49 +02:00
Sebastiaan Janssen 99b2a2da38 #U4-5337 fixed Due in version: 7.1.5,6.2.2
Too easy to lock yourself out of the backoffice
2014-08-12 11:06:24 +02:00
Sebastiaan Janssen db22dfcf7d #U4-5337 fixed Due in version: 7.1.5,6.2.2
Too easy to lock yourself out of the backoffice
2014-08-12 11:05:25 +02:00
Sebastiaan Janssen 15ed458ad3 #U4-5258 Fixed
RelationService AreRelated should accepts two entity IDs and a relationTypeAlias
2014-08-12 10:25:33 +02:00
Sebastiaan Janssen 9fc51d8a12 #U4-5258 Fixed
RelationService AreRelated should accepts two entity IDs and a relationTypeAlias
2014-08-12 10:23:11 +02:00
AndyButland dc32138006 Set up database level paging for retrieving child content for list view 2014-08-12 08:15:43 +01:00
jakobdyrby 1a3d3994c3 added some ToArray() to ensure execution 2014-08-11 12:35:55 +02:00
jakobdyrby 4aedfc419e copy past error 2014-08-11 10:07:27 +02:00
jakobdyrby 21c6cf71cb fixed bug where root element is documents and not documentSet 2014-08-11 09:52:16 +02:00
Shannon 247b5b648d Merge branch '7.1.5_U4-5316' of https://github.com/bjarnef/Umbraco-CMS into bjarnef-7.1.5_U4-5316
Conflicts:
	src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.html
	src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/multicolorpicker.controller.js
2014-08-08 12:29:17 -06:00
Shannon fa4a268556 Updates some naming conventions and code formatting 2014-08-08 12:08:34 -06:00
Shannon 1449966537 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-08-08 12:00:35 -06:00
Shannon 24a6ae4e32 Fixes: U4-4634 Image preview in Media is stretched 2014-08-08 12:00:30 -06:00
per ploug d2f9bd6896 Fixed: U4-5324 Users with a content root node, cant have custom tree roots 2014-08-08 10:54:05 +02:00
Shannon 6e3d0b5d91 fixes tests 2014-08-07 11:15:34 -06:00
Shannon bf58359eed fixed tests 2014-08-07 11:10:40 -06:00
Shannon Deminick 59b4940979 Merge pull request #439 from AndyButland/u4-5290
U4-5290 - allow for retrieval of a tag's count as well as it's text
2014-08-06 18:58:00 -06:00
AndyButland 8f2f4b579e Added unit test for tags NodeCount property 2014-08-06 22:38:46 +02:00
AndyButland 5efabee0bd Added ignore attribute to NodeCount property on TagDto 2014-08-06 22:38:14 +02:00
Shannon fc19748fbf Fixes: U4-5304 7.1.4 Media Picker can't view images RangeError: Maximum call stack size exceeded 2014-08-06 11:20:24 -06:00
AndyButland 6ea64c84d8 Removed parameter for requesting count for tags and made including the count the default behaviour 2014-08-06 07:36:25 +02:00
Shannon 7dfa633fb3 Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Core/Sync/DefaultServerMessenger.cs
	src/Umbraco.Tests/Services/ContentServiceTests.cs
2014-08-05 12:49:51 -06:00
Shannon 6ce303a918 Completes: U4-5264 Add Count methods to IContentService, IMediaService, etc... 2014-08-05 12:37:20 -06:00
Shannon 62d3dc78fa Ensures all caching is disabled for editing templates (master pages and views) 2014-08-05 10:59:54 -06:00
Shannon 3374f9e023 Ensures that the batched distributed calls doesn't cache refresh the server executing the distributed calls since tht will already be done locally. 2014-08-05 10:32:07 -06:00
Stephan 2675992d82 Merge branch '6.2.2' into 6.2-4931 2014-08-05 18:20:04 +02:00
Stephan 0a321e37fc Merge branch '7.1.5' into 7.1-4931 2014-08-05 18:15:51 +02:00
Stephan f3ca9e067b Merge remote-tracking branch 'origin/7.1.5' into 7.1.5 2014-08-05 18:08:20 +02:00
Stephan 4814fb3e96 U4-5276 - get package installer to respect binding redirects 2014-08-05 18:07:50 +02:00
Stephan ce29f54663 U4-4837 - bugfix Umbraco.Core.EnumerableExtensions.InGroupsOf 2014-08-05 17:52:43 +02:00
Shannon 7261162d8a Fixes some of U4-2633 Bundle all cache refresher transmissions into a single call per request for much better performance. See last note on issue. 2014-08-04 17:50:19 -06:00
Shannon Deminick 5f9b6c14f4 Merge pull request #441 from countrywide/U4-5110
U4-5110 Prevent IE Compatibility Mode
2014-08-04 10:59:27 -06:00
Shannon Deminick 7685e53cfe Merge pull request #440 from countrywide/7.1.5
Fix for issue U4-4162 - MacroContainer editing
2014-08-04 10:56:02 -06:00
Declan Cook 60e81a051d U4-5110 Prevent IE Compatibility Mode 2014-08-04 16:18:43 +01:00
Declan Cook b2436bd9b6 Fix for issue U4-4162 - MacroContainer editing
Allows editing of first element in a macro container.
2014-08-04 16:07:07 +01:00
Sebastiaan Janssen 1c83114c78 Merge pull request #436 from lucasch/U4-5109
U4-5109 - Added an icon for the Visual Blocks tinymce plugin to the skins CSS files
2014-08-04 09:11:54 +02:00
Sebastiaan Janssen 46c07e55e4 Merge pull request #438 from AndyButland/u4-5221
U4-5221 - Ensured multi-value data type prevalues are rendered sorted according to defined sort order
2014-08-04 08:57:35 +02:00
AndyButland 6ee078108d Added NodeCount property to ITag/Tag and an optional flag to tag retrieval methods on TagsService, in order to retrieve the count for the tag as well as it's text 2014-08-03 23:09:56 +02:00
AndyButland 90eb5dc7ac Ensured multi-value data type prevalues are rendered sorted according to defined sort order 2014-08-03 20:00:04 +02:00
user d6833c9b86 Updated the Umbraco TinyMCE skin to use the templates icon for visual blocks,
Visual blocks had no icon in the css
2014-08-01 11:58:33 -05:00
Shannon Deminick 2325c8a64f Merge pull request #434 from naepalm/7.1.5
Updated the dictionary table icons and the CSS
2014-07-31 18:46:29 -06:00
Sebastiaan Janssen d4e205a47a Merge branch 'AndyButland-wip-u4-3963' into 7.1.5 2014-07-31 12:43:03 +02:00
Sebastiaan Janssen 84df6e5315 Merge branch 'wip-u4-3963' of https://github.com/AndyButland/Umbraco-CMS into AndyButland-wip-u4-3963 2014-07-31 12:42:45 +02:00
Janae Cram af3b655073 Updated the dictionary table icons and the CSS to make the section more usable. 2014-07-30 09:12:06 -07:00
Shannon f8e366bb4a fixes minor bool check in js for jslint 2014-07-29 11:23:58 -06:00
Shannon Deminick 6439d7337d Merge pull request #433 from arknu/rte_imagesizing
U4-4729: Make max image dimension in RTE configurable
2014-07-29 10:59:43 -06:00
Stephan 56dddbca1c U4-5275 - use IPublishedContentModelFactory for members 2014-07-29 10:56:49 +02:00
Stephan 53ea491bd6 U4-5286 - add property converter for color picker 2014-07-29 09:52:00 +02:00
Shannon b5f900e789 added a couple of tests for member names 2014-07-28 19:37:44 -07:00
arknu cad946905c Bugfix: 0 value was ignored 2014-07-27 13:06:02 +02:00
arknu 49c26f4a5e Followup: make sure that defualt value is applied in RTE 2014-07-27 12:46:52 +02:00
arknu 45b173c0db U4-4729: Make max image dimension in RTE configurable 2014-07-27 01:25:39 +02:00
Stephan 5165d70fbb Merge branch '7.1.5' into 7.1-4931 2014-07-25 11:05:31 +02:00
Stephan c2816e759b Fail IPublishedContentFactory faster 2014-07-25 11:00:56 +02:00
Shannon Deminick 7ab8ea4da4 Merge pull request #432 from AndyButland/u4-5138
U4 5138 - fixed a couple of issues with list view styling and function
2014-07-24 15:24:51 -07:00
Shannon 1e8f016234 Fixes: U4-5111 7.1.4 JavaScript runtime error: 'UmbClientMgr' is undefined 2014-07-24 15:13:41 -07:00
Shannon d07ba12e73 Fixes merge 2014-07-24 14:50:06 -07:00
Shannon 7d7fa0e389 Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
	src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs
	src/Umbraco.Web/Umbraco.Web.csproj
2014-07-24 09:42:44 -07:00
Shannon fdab6d5de5 Merge branch '6.2.2' of https://github.com/umbraco/Umbraco-CMS into 6.2.2 2014-07-24 09:39:12 -07:00
Shannon ff94eba3a7 Fixes: U4-5273 6.2: Umbraco.Field no longer rendering macro container 2014-07-24 09:39:05 -07:00
Shannon 834de2099f updates manifest watcher just to ensure we don't try to shut down the app domain more than once. 2014-07-24 09:06:45 -07:00
Shannon 904ee40a34 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-07-24 08:57:07 -07:00
Shannon 99973841e3 Fixes up packaging service - problem with setting property values properly on import due to a wip portion of getting tag values set correctly, though this was solved at the repo level and i unfortunately forgot to re-update this code. 2014-07-24 08:57:02 -07:00
Sebastiaan Janssen e200e2fc48 Removes obsolete cachebrowser 2014-07-24 16:17:40 +02:00
Sebastiaan Janssen 69c7ecad59 Got bitten by stray files that were left behing in _BuildOutput folder, clean before build 2014-07-24 14:43:40 +02:00
Sebastiaan Janssen b1350fa4a6 Customize button was displayed lower than installer button 2014-07-24 14:40:33 +02:00
Sebastiaan Janssen 6014608d5a Microsoft.AspNet.WebApi.Host doesn't exist ;) 2014-07-24 14:24:13 +02:00
Shannon 929180224f Merge remote-tracking branch 'origin/6.2.2' into 7.1.5 2014-07-23 14:07:47 -07:00
Shannon 22dde5a421 U4-4738 Backoffice search should support node ID search 2014-07-23 14:07:21 -07:00
Shannon ee5c532be7 Fixes: U4-4738 Backoffice search should support node ID search 2014-07-23 14:07:05 -07:00
Shannon c9b27b5a70 Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Web/Umbraco.Web.csproj
2014-07-23 12:32:53 -07:00
Shannon 7f510c107a updates proj's with correct specific version attributes 2014-07-23 12:31:38 -07:00
Shannon 2ebfd8f3d4 updates nuspec to have latest versions for all dependencies 2014-07-23 12:31:16 -07:00
Shannon 6e20be993b updates nuspec to have latest versions for all dependencies 2014-07-23 12:31:03 -07:00
Shannon 84fad1300b All nuget refs are now up to date and installed correctly with copy local set correctly for all non gac 4.5 assemblies as well as the specific version attribute. 2014-07-23 12:19:40 -07:00
Shannon fd7930256b Uninstalls package CodeSharp.Package.AspNetWebPage and removes refs to Nuget.Core and Webpages.Administration - neither of which are used anywhere... but we've been shipping Umbraco with them for some reason. 2014-07-23 10:53:30 -07:00
Shannon 6319b10881 Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/SQLCE4Umbraco/SqlCE4Umbraco.csproj
	src/SQLCE4Umbraco/app.config
	src/Umbraco.Core/app.config
	src/Umbraco.Tests/App.config
	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
	src/Umbraco.Web/Umbraco.Web.csproj
	src/Umbraco.Web/app.config
	src/umbraco.MacroEngines/App.Config
	src/umbraco.businesslogic/app.config
	src/umbraco.datalayer/app.config
	src/umbraco.editorControls/app.config
2014-07-23 10:08:01 -07:00
Shannon c24775adb0 re-installs mvc, webapi and ensures running the latest 2.0.x versions of webpages and razor, now all proj's are consistent with correct versions referenced and copy local set correctly. 2014-07-23 10:03:26 -07:00
Shannon 0d164ffb08 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-07-23 09:04:01 -07:00
Shannon dd83dced4c Fixes up back office search, U4-5048, U4-5021, U4-5167, U4-5194 2014-07-23 09:03:54 -07:00
Sebastiaan Janssen ea56363f6d Adds logging to the RteEmbedController when there's an exception, instead of silently swallowing the error 2014-07-23 14:04:50 +02:00
Sebastiaan Janssen f86902bc89 Microsoft.Bcl 1.1.7 requires NuGet 2.8.1 - also registering MyGet feed from the build.bat file so that it actually works for everybody 2014-07-23 11:39:40 +02:00
Shannon b207521f4b Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
	src/Umbraco.Web/Umbraco.Web.csproj
2014-07-22 16:15:02 -07:00
Shannon a2aaa9a1ea updates proj references to have SpecificVersion set to false 2014-07-22 16:14:13 -07:00
Shannon 217f591db0 updates proj references to have SpecificVersion set to false for libs 2014-07-22 16:13:26 -07:00
Shannon b835cfb76d Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
	src/Umbraco.Web.UI/packages.config
2014-07-22 15:55:06 -07:00
Shannon 82cfbab17a Ensures all WebApi DLLs are copied local 2014-07-22 15:51:44 -07:00
Shannon d453aedd0d Microsoft.Net.Http required a reinstall command for the UI project as noted by it's persisted 'requiresReinstall' attribute in the packages.config - also ensures that ALL of the WebApi DLLs are copied local 2014-07-22 15:51:13 -07:00
Shannon 851181f861 Microsoft.Net.Http required a reinstall command for the UI project as noted by it's persisted 'requiresReinstall' attribute in the packages.config - and then the project wouldn't actually build anymore. 2014-07-22 15:39:07 -07:00
Shannon Deminick 4680b643f6 Merge pull request #426 from arknu/searchimprovements
Fix U4-5194 and U4-5167: Handle multiple spaces and quoted searches in the backoffice correctly
2014-07-22 15:26:38 -07:00
Shannon 05be2a5e86 adds test 2014-07-22 15:13:01 -07:00
AndyButland 8445c9c29b Applied styling to list view paging (active class on page numbers, disabled on prev and next when not available 2014-07-22 23:11:22 +01:00
AndyButland e8eea039e1 Reset page number to one when applying filter on list view 2014-07-22 22:35:58 +01:00
Shannon cd8b8dc704 Fixes: U4-5212 Preview button only greyed and not disabled while previewing. (was save button) 2014-07-22 11:33:58 -07:00
Shannon e98ed8561a Fixes: U4-5252 2014-07-23 04:10:48 +10:00
Shannon 626e155c07 Fixes SO issue when tags contain an empty string 2014-07-23 03:54:27 +10:00
Shannon Deminick de968a18dd Merge pull request #431 from AndyButland/u4-5097
U4-5097 - fixed issue with inital value of slider not being saved
2014-07-23 01:47:43 +10:00
AndyButland 8d1d2f2414 Initialised slider scope model value if not already set from a previous save 2014-07-21 22:27:04 +02:00
Stephan 4bb38609ad Merge pull request #429 from leekelleher/U4-5253
U4-5253 PublishedContentModelFactory case-sensitive dictionary
2014-07-21 16:45:13 +02:00
leekelleher 0370a1bcf2 PublishedContentModelFactory: Changes the private 'constructors' dictionary to be case-insensitive. 2014-07-21 15:32:16 +01:00
arknu 188613e5e7 U4-5167: Handle quoted searches in the backoffice correctly 2014-07-18 17:42:47 +02:00
arknu a9261d217e U4-5194: Fix handling of multiple words in backoffice search 2014-07-18 17:00:59 +02:00
Shannon 4693fb23c8 removes unneeded lock on master controller factory - must have been left over from some v5 stuff for some reason :( this will speed things up a bit! 2014-07-18 16:28:54 +10:00
Shannon 6e78a9f98f fix merge 2014-07-16 14:14:02 +10:00
Shannon 92c19ba76c Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Core/IO/UmbracoMediaFile.cs
2014-07-16 14:13:05 +10:00
Shannon 7848a3095d Fixes: U4-5207 User Type with a space in the name breaks User Content 2014-07-16 14:12:04 +10:00
Shannon 1a75e7beb1 Fixes: U4-5207 User Type with a space in the name breaks User Content 2014-07-16 14:10:24 +10:00
Shannon 388eea95e5 fixes a single vs first check with render route handler when there might be duplicate routes assigned to a controller, fixes short/int parse 2014-07-16 13:46:15 +10:00
Shannon fdf1218a9e fixes another short/int parse 2014-07-16 13:43:08 +10:00
Shannon 1a1aba92ef Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-07-16 13:41:11 +10:00
Shannon 39ee22cbd6 fixes a single vs first check with render route handler when there might be duplicate routes assigned to a controller 2014-07-16 13:41:02 +10:00
Shannon Deminick 119acaa28a Merge pull request #425 from jbreuer/patch-3
Fix for U4-5223
2014-07-16 13:36:30 +10:00
Jeroen Breuer e22282c1e8 Fix for U4-5223 2014-07-15 14:24:27 +02:00
Shannon 6167d1584a Fixes an xss vulnerability 2014-07-15 11:37:31 +10:00
Shannon 0c2d1f72b9 Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-07-15 11:36:13 +10:00
Shannon 8c36dac67a Fixes an xss vulnerability 2014-07-15 11:36:03 +10:00
Stephan d7f8b10b28 Merge pull request #424 from semmeo/7.1.5
Update fr.xml
2014-07-11 12:36:20 +02:00
Semmeo 7e8be5fd67 Update fr.xml
Updated french umbraco translation from scratch, by translating en.xml. Tested and working.
2014-07-11 12:31:20 +02:00
Stephan e515b716f0 U4-4931 - also for ObjectCacheRuntime provider
Conflicts:
	src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
2014-07-10 10:40:20 +02:00
Stephan fd49c81940 U4-4931 - fixing & refactoring
Conflicts:
	src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs
2014-07-10 10:39:52 +02:00
Stephan cc6099c953 U4-4931 - fixing & refactoring
Conflicts:
	src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs
	src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs
	src/Umbraco.Core/CacheHelper.cs
	src/Umbraco.Core/Umbraco.Core.csproj
	src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs
2014-07-10 10:39:28 +02:00
Stephan fdadd80414 U4-4931 - do not cache exceptions
Conflicts:
	src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs
	src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs
2014-07-10 10:37:46 +02:00
Stephan fcd7e9517d U4-4931 - fixing & refactoring
Conflicts:
	src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs
	src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs
	src/Umbraco.Core/Cache/DictionaryItemWrapper.cs
	src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs
	src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
2014-07-10 10:37:15 +02:00
Stephan 72f53e4645 U4-4931 - fixing & refactoring
Conflicts:
	src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
2014-07-10 10:36:10 +02:00
Stephan 2c9a287bda U4-4931 - fixing & refactoring 2014-07-10 10:35:40 +02:00
Stephan e2f4cfaf82 U4-4931 - fixing & refactoring
Conflicts:
	src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs
	src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs
	src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
2014-07-10 10:35:12 +02:00
Stephan 8729156fbf U4-4931 - lock contention & potential application lockdown
Conflicts:
	src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
	src/umbraco.cms/Actions/Action.cs
2014-07-10 10:34:17 +02:00
Stephan bfd0c5e309 U4-4931 - also for ObjectCacheRuntime provider 2014-07-10 10:25:51 +02:00
Stephan 43669e62b4 Merge branch '6.2.2' into 6.2-4931 2014-07-10 10:25:12 +02:00
Shannon 6b128ea7d4 Fixes: U4-5208 RenderActionInvoker needs to be able to run async so needs to inherit from AsyncControllerActionInvoker 2014-07-10 17:29:34 +10:00
Shannon 60d6ae44b5 Fixes: U4-5208 RenderActionInvoker needs to be able to run async so needs to inherit from AsyncControllerActionInvoker 2014-07-10 17:28:33 +10:00
Shannon b3035d160c adds another test 2014-07-10 13:48:32 +10:00
Shannon f27f458a5d Adds another test 2014-07-10 13:46:31 +10:00
Shannon 3f09453345 Fixes: U4-5198 Tags still appearing from items tagged in recycle bin 2014-07-10 13:40:22 +10:00
Shannon 4857107b5e Fixes: U4-4852 ContentService can add new permissions but not remove them 2014-07-10 12:24:36 +10:00
Shannon 080bf7293d replaces the tuple collection for bulk permission assignment to use a new model: EntityPermissionSet 2014-07-10 12:11:47 +10:00
Shannon b3288f0c91 fixes merge issue 2014-07-10 12:11:10 +10:00
Shannon d5993a4783 Backports fix from 7 for GetDimensions call to ensure the file exists 2014-07-10 12:09:26 +10:00
Shannon b254f81f4a Merge remote-tracking branch 'origin/6.2.2' into 7.1.5 2014-07-10 11:54:05 +10:00
Shannon 17b0547cfc Merge commit '874f464a2b51000920e2323f93bea116546e9b88' into 7.1.5
Conflicts:
	src/Umbraco.Core/IO/FileSystemExtensions.cs
	src/Umbraco.Core/IO/UmbracoMediaFile.cs
	src/Umbraco.Web/umbraco.presentation/umbraco/channels/UmbracoMetaWeblogAPI.cs
2014-07-10 11:53:45 +10:00
Shannon 86c2a6e8a9 updates physical file system, now that files are being disposed of properly we don't need to delete first 2014-07-10 11:51:48 +10:00
Shannon 874f464a2b Fixes: U4-5190 Overwrite Media File in 6.2.1 - process cannot access along with a bunch of other undisposed disposables :/ 2014-07-10 11:45:57 +10:00
Shannon 93151a43ac Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs
	src/Umbraco.Web/Security/MembershipHelper.cs
	src/Umbraco.Web/umbraco.presentation/content.cs
2014-07-10 11:14:20 +10:00
Shannon b3c41e051f Fixes: U4-5199 Appending certain query strings to URLs cause InvalidOperationException (The UmbracoContext.Current is null) 2014-07-10 11:10:49 +10:00
Shannon a88637910f updates content xml bits so if it's called outside a web req it still executes.
Conflicts:
	src/Umbraco.Web/umbraco.presentation/content.cs
2014-07-10 10:45:26 +10:00
Shannon 1af7649a87 updates content xml bits so if it's called outside a web req it still executes. 2014-07-10 10:44:40 +10:00
Shannon 431e93000e Fixes: U4-5157 Membership.GetUser() causes 13 SQL queries, including duplicates 2014-07-08 19:04:38 +10:00
Shannon f83bbdbf90 Fixes: U4-5157 Membership.GetUser() causes 13 SQL queries, including duplicates 2014-07-08 18:54:36 +10:00
Shannon 5a810b584e Fixing: U4-5157 Membership.GetUser() causes 13 SQL queries, including duplicates 2014-07-08 18:07:59 +10:00
Shannon bcae3afa45 fixes null check on tag extractor 2014-07-07 16:21:53 +10:00
Shannon 3aff474648 Fixes: #U4-4685 Default sorting of listview items 2014-07-07 16:21:12 +10:00
Shannon 1a821b4073 super quick performance win in the case where people don't pass in distinct values 2014-07-01 17:41:22 +10:00
Shannon efda233fcf super quick performance win in the case where people don't pass in distinct values 2014-07-01 17:40:41 +10:00
Shannon ed2e409707 Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/Umbraco.Web/Security/MembershipHelper.cs
2014-07-01 12:54:48 +10:00
Shannon 8645e0b193 Fixes: U4-5140 Tags are not respected with bulk publish operations - just need to ensure all tests are passing and run some local tests 2014-07-01 12:53:07 +10:00
Shannon a89b9e9da6 ignores log4net tests 2014-07-01 11:16:57 +10:00
Shannon 8917701fa3 ignores failing test - need stephane to look 2014-07-01 11:03:47 +10:00
Shannon 68b17bff08 Ensures no membership user writing when checking public access 2014-07-01 10:52:29 +10:00
Shannon 9b2b961f83 Ensures no membership user writing when checking public access 2014-07-01 10:51:56 +10:00
Shannon 7a12060c37 Potentially fixes U4-5069 Deadlock occurring in MemberService.GetByUsername
Conflicts:
	src/Umbraco.Web/Security/MembershipHelper.cs
2014-07-01 10:46:52 +10:00
Shannon f3c7533796 Potentially fixes U4-5069 Deadlock occurring in MemberService.GetByUsername 2014-07-01 10:43:44 +10:00
Shannon 59116fc7fa un-does the tag export/import, instead need to look at the internal of a simple publish (without save) to see if tags get written or not. 2014-06-30 17:05:34 +10:00
Shannon a66670263f fixes null check 2014-06-30 17:04:59 +10:00
Shannon 1e1d49f305 fixes U4-5140 Package installation for content does not respect tags, however need to check with Morten if it's required because if we never actually publish content on package install then this wouldn't really matter. But surely we must publish on package install for some thing because starter kits are published... hrm. 2014-06-30 16:43:51 +10:00
Shannon 1be2706c30 fix merge 2014-06-30 15:09:41 +10:00
Shannon eaf2956524 Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	src/umbraco.cms/businesslogic/Packager/PackageInstance/CreatedPackage.cs
2014-06-30 14:26:33 +10:00
Shannon 525280d466 fixed some tests 2014-06-30 14:09:36 +10:00
Shannon 496beca8f2 This Can_Find_Targetted_Migrations test needed a valid database to pass. This update ensures that the database is created with the old version 4.8 db schema. 2014-06-30 14:02:43 +10:00
Stephan 637a22293d U4-4931 - fixing & refactoring 2014-06-28 18:33:31 +02:00
Stephan 3c8517edcb Merge branch '6.2.2' into 6.2-4931 2014-06-28 16:26:44 +02:00
Stephan 45e6127d00 U4-4866 - fix the build 2014-06-28 16:26:27 +02:00
Stephan 6eb5d3f7ef Merge branch '6.2.2' into 6.2-4931 2014-06-28 16:06:30 +02:00
Stephan e6986432df U4-4931 - fixing & refactoring 2014-06-28 12:24:02 +02:00
Sebastiaan Janssen 5bee398d09 #U4-3838 fixed Due in version 7.1.5
Fixes ContentService Copy uses IOHelper.MapPath but should use fs.GetFullPath and a few other instances where IOHelper was used
2014-06-27 17:22:51 +02:00
Sebastiaan Janssen c53d812742 #U4-3154 Fixed Deleting single media item in recycle bin does not use the FileSystemProvider 2014-06-27 16:26:22 +02:00
Sebastiaan Janssen 966a29954c #U4-5010 fixed
Package editing view missing in distribution
2014-06-27 16:25:57 +02:00
Morten Christensen 9f28aea10d This Can_Find_Targetted_Migrations test needed a valid database to pass. This update ensures that the database is created with the old version 4.8 db schema. 2014-06-27 13:23:54 +02:00
Morten Christensen 097014454f Fixing issue in MacroRepository which was causing a test to fail.
Changing the alias of a macro property didn't alias work, so added a fallback to the Id of the property in the cases where the Alias wouldn't work.
2014-06-27 12:42:15 +02:00
Morten Christensen 48143bc0c3 Minor updates to the Package installation and extraction as part of PR #417 2014-06-27 12:37:56 +02:00
Morten Christensen 75c26e4d92 Merge pull request #417 from netmester/package_installer_service
Package installer service
2014-06-27 10:58:10 +02:00
Shannon ad4135f629 does a toarray to make sure it doesn't reiterate 2014-06-27 14:04:35 +10:00
Shannon 8e7ed865f0 Fixes: U4-5151 Backoffice controllers collide on name even if different namespace 2014-06-27 13:34:15 +10:00
Shannon f627de2433 tweaks orange 2014-06-26 17:38:24 +10:00
Shannon 303aa07235 migrates most of the xml serialization from the packaging service to the EntityXmlSerializer 2014-06-26 17:32:38 +10:00
Shannon b404cc6438 Fixes: U4-5141 Packager doesn't respect 'allow at root' or 'list view' 2014-06-26 17:18:00 +10:00
Shannon 80103e6155 Fixes: U4-4866 Select content root when creating a package 2014-06-26 16:55:44 +10:00
Shannon 4fbf65e684 Fixes: U4-4866 Select content root when creating a package 2014-06-26 16:45:59 +10:00
Shannon 0aeac407c5 removes extremely silly try catch all when using the packager - why was this here !? So that nobody would ever know why creating their package failed? 2014-06-26 16:33:56 +10:00
Shannon be98ceea6c Fixes: U4-5011 Auto collapsing tree structure 2014-06-26 16:14:50 +10:00
Shannon 07e0cd2957 uses a better orange 2014-06-26 14:08:51 +10:00
Shannon 9da7d56b9c Fixes styling on installer alert box so it's contained within the pane 2014-06-26 13:58:09 +10:00
Shannon 5a3890159f Fixes: U4-5142 Fix the horrible orange warning color in the back office 2014-06-26 13:52:59 +10:00
Shannon 70da51113c Streamlines form error colors to be inline with the rest of our color scheme 2014-06-26 13:31:39 +10:00
Shannon 0f7c407a7c Merge remote-tracking branch 'origin/6.2.2' into 7.1.5
Conflicts:
	build/Build.bat
	build/NuSpecs/UmbracoCms.Core.nuspec
	build/NuSpecs/UmbracoCms.nuspec
	src/Umbraco.Core/Configuration/UmbracoVersion.cs
	src/Umbraco.Core/Services/PackagingService.cs
	src/Umbraco.Tests/Services/LocalizationServiceTests.cs
	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
	src/Umbraco.Web/umbraco.presentation/content.cs
2014-06-26 11:51:25 +10:00
Shannon 392634eb76 bumps nuspec versions 2014-06-26 11:44:28 +10:00
Shandem 7bc5588d6d Merge pull request #420 from bjarnef/7.1.5_U4-3953
Fix for U4-3953
2014-06-26 11:38:25 +10:00
Shannon 259bc863f0 Removes Examine.dll deploy from nuspec 2014-06-26 11:32:43 +10:00
bjarnef 70514b88c5 Fix for U4-3953
Installing package - installing message progressbar causes horizontal
scrollbar
2014-06-26 02:57:47 +02:00
Shannon 582bafdefd Updates version 2014-06-26 10:02:09 +10:00
Shannon 9924ef2381 Merge branch '6.2.1' into 6.2.2 2014-06-26 09:57:16 +10:00
Morten Christensen 12cdd3326a The CopyFile extension method was not disposing the file stream, so any attempt to delete the source file afterwards would fail.
Removing unused using statements.
2014-06-25 16:00:21 +02:00
Shannon 859fbaaa05 Fixes: U4-5121 umbraco.content launches threadpool threads to reload the xml cache which causes lots of other issues
Conflicts:
	src/Umbraco.Web/umbraco.presentation/content.cs
2014-06-25 19:02:49 +10:00
Shannon 27764a2c82 merge issue 2014-06-25 18:58:29 +10:00
Shannon 5fdcec6904 Fixes: U4-5121 umbraco.content launches threadpool threads to reload the xml cache which causes lots of other issues 2014-06-25 18:56:51 +10:00
Shannon 0b678f1d3b Merge remote-tracking branch 'origin/6.2.1' into 7.1.5
Conflicts:
	src/umbraco.cms/businesslogic/Packager/Installer.cs
2014-06-25 15:59:20 +10:00
Shannon 72991334c4 little bit of code cleanup 2014-06-25 15:55:52 +10:00
Shannon 309adbfb20 Merge remote-tracking branch 'origin/6.2.1' into 7.1.5
Conflicts:
	src/Umbraco.Tests/Umbraco.Tests.csproj
2014-06-25 14:36:09 +10:00
Shannon c659d5b85e fixes dll ref 2014-06-25 14:35:14 +10:00
Shannon 3ecc974325 Merge remote-tracking branch 'origin/6.2.1' into 7.1.5
Conflicts:
	build/NuSpecs/UmbracoCms.Core.nuspec
	src/Umbraco.Tests/Umbraco.Tests.csproj
	src/Umbraco.Tests/packages.config
	src/Umbraco.Web.UI/packages.config
2014-06-25 14:33:32 +10:00
Shannon a4bf4b0977 Fixes: U4-5118 Examine not storing XML fragments - updates to latest examine 2014-06-25 14:19:31 +10:00
Shannon bef6ce66f1 Updated core nuspec to include Examine 2014-06-25 14:19:06 +10:00
Shannon a18a2198a8 removes comment 2014-06-25 12:25:27 +10:00
Shannon 38c4bc72cd Merge remote-tracking branch 'origin/6.2.1' into 7.1.5 2014-06-25 12:03:53 +10:00
Shannon 7cf7ac687c last issue with state/keep alive timer 2014-06-25 12:03:31 +10:00
Shannon 6b626baffb Merge remote-tracking branch 'origin/6.2.1' into 7.1.5 2014-06-25 11:56:56 +10:00
Shannon 7ce0272169 updates state param with timers to use singleton 2014-06-25 11:56:36 +10:00
Shannon acb934e3f1 ensures timer's are not GCd 2014-06-25 11:52:48 +10:00
Shannon 6485e625b1 Merge remote-tracking branch 'origin/6.2.1' into 7.1.5 2014-06-25 11:37:21 +10:00
Shannon 3aa486a463 another log 2014-06-25 11:36:58 +10:00
Shannon 5aa0621c90 adds some notes 2014-06-25 11:36:14 +10:00
Shannon 625b8f7e65 Ensures the 'Index' portion of the path is used for scheduled publishing 2014-06-25 11:26:08 +10:00
Shannon 72b7b443e9 Merge remote-tracking branch 'origin/6.2.1' into 7.1.5 2014-06-25 11:02:04 +10:00
Shannon 53c1c97aeb Merge branch '7.1.5' of https://github.com/umbraco/Umbraco-CMS into 7.1.5 2014-06-25 11:01:03 +10:00
Shannon 5011eee037 fixes merge issue 2014-06-25 11:00:53 +10:00
Shannon e27163f3a7 adds logging to scheduled bits 2014-06-25 11:00:11 +10:00
Shandem 22bace9545 Merge pull request #419 from imulus/fix/u4-5128
Support virtual paths when defining PropertyEditors via Attributes (U4-5128)
2014-06-25 09:53:31 +10:00
jakobdyrby 8c36a683e6 Change some class from public to internal 2014-06-24 11:27:04 +02:00
Tom Fulton a23fb7bc74 Support virtual paths when defining PropertyEditors via Attributes (U4-5128) 2014-06-23 12:16:14 -04:00
Shannon 2ccec3be33 Merge remote-tracking branch 'origin/6.2.1' into 7.1.5
Conflicts:
	src/Umbraco.Core/Umbraco.Core.csproj
	src/Umbraco.Web/LegacyScheduledTasks.cs
	src/Umbraco.Web/Umbraco.Web.csproj
	src/Umbraco.Web/WebBootManager.cs
	src/Umbraco.Web/umbraco.presentation/publishingService.cs
2014-06-23 15:08:04 +10:00
Shannon 65ea6f94d3 adds authorization to the scheduled publishing , this new attribute can be used to secure scheduled tasks as well. 2014-06-23 15:02:23 +10:00
Morten Christensen 3af3e054d7 Minor refactoring of the MigrationRunner to allow a bit more flexibility for using it for non-Core migrations.
Allowing the list of migrations to be changed through the two migration events using the MigrationEventArgs.
2014-06-20 12:57:33 +02:00
Shannon 9158ea1ace fixes url returned in GetCurrentServerUmbracoBaseUrl 2014-06-20 14:45:45 +10:00
Shannon e8f7f77bb6 Fixes: U4-581 Automatic publishing not working in load balanced setup - added some more convention and configuration to distributed calls so that servers are aware of the master and how to call into themselves for scheduled tasks, ping and scheduled publishing. Will need to update the docs on LB regarding this too. Cleaned up the code that does the scheduling and separates it into proper segments. Obsoletes the old presentation classes that were doing it. 2014-06-20 14:34:21 +10:00
Shannon 6b5cb3a9cf Fixes: U4-4947 Tag Editor twitter typeahead 2014-06-19 18:04:32 +10:00
Shannon bb655afd72 Fixes: U4-5080 Error deleting property type that references tags 2014-06-19 17:39:31 +10:00
Shannon ad3502d438 Fixes: U4-5004 Pressing escape in the login screen causes a blank screen 2014-06-18 19:28:16 +10:00
Shannon dfb40802ff makes grunt vs and grunt dev a little faster 2014-06-18 17:31:00 +10:00
Shannon 8ded6fff93 adds vs task to grunt 2014-06-18 17:15:09 +10:00
Shannon 56111be82d Properly fixes: U4-4893 Inconsistent display of file extensions in the UI 2014-06-18 17:11:47 +10:00
Stephan 75e003185a U4-4931 - do not cache exceptions 2014-06-17 19:34:24 +02:00
Stephan 9d12e1c7df Merge branch '6.2.2' into 6.2-4931 2014-06-17 19:19:17 +02:00
Shannon f25463babe Merge remote-tracking branch 'origin/6.2.1' into 7.1.5 2014-06-17 19:05:22 +10:00
Shannon b8575ef158 Started writing test implementation to mimic what developers might test in their controllers to expose the pitfalls of umbraco testing so we can make this simpler - identifying what needs to be public and how to re-strcuture some objects constructors, etc... to simplify and make this possible. 2014-06-17 18:53:23 +10:00
jakobdyrby 3e0cd0a605 Now it works with a notimplimentet exception 2014-06-17 09:13:54 +02:00
Shannon 1c23940b0b Merge remote-tracking branch 'origin/6.2.1' into 7.1.5
Conflicts:
	src/Umbraco.Tests/packages.config
	src/Umbraco.Web.UI/packages.config
2014-06-17 16:09:29 +10:00
Shannon d3bb750f98 Updated to latest Examine with lock fixes 2014-06-17 16:07:08 +10:00
jakobdyrby 7ec13146b2 more revert 2014-06-17 07:34:49 +02:00
jakobdyrby 48e0e5da75 revert to org files 2014-06-17 07:31:22 +02:00
jakobdyrby 448bfc166f revert to org files 2014-06-17 07:12:59 +02:00
jakobdyrby 7b48a59ca2 added events 2014-06-17 07:06:07 +02:00
Shandem 1f161b2b9c Merge pull request #395 from Excommunicated/7.1.5
Close user dialog on logout
2014-06-17 14:07:45 +10:00
Morten Christensen 6f27f839e0 Related to U4-4199 lets get rid of all these Console.WriteLine's 2014-06-16 16:29:15 +02:00
jakobdyrby f7e046deaf Undo some non changes 2014-06-16 14:01:59 +02:00
jakobdyrby 09b34625b9 removed change to src/Umbraco.Core/Services/DataTypeService.cs 2014-06-16 13:58:50 +02:00
jakobdyrby 3b3e412450 Undo some changes 2014-06-16 12:29:32 +02:00
jakobdyrby 3c6bf1ce92 Removed some complex objects that didn't need to be complex 2014-06-16 12:13:01 +02:00
jakobdyrby dfb08b9ec7 Removed IStyleSheet 2014-06-16 12:13:00 +02:00
jakobdyrby eef7ce45d3 cleanup 2014-06-16 12:13:00 +02:00
jakobdyrby 697d4e3b23 more logical code 2014-06-16 12:12:59 +02:00
jakobdyrby 6d77121c05 Test for support stylesheets 2014-06-16 12:12:58 +02:00
jakobdyrby a7928f87ee small ajustments 2014-06-16 12:12:58 +02:00
jakobdyrby 07d431f52e more moving 2014-06-16 12:12:57 +02:00
jakobdyrby bb71efd4fa Move moving classes around 2014-06-16 12:12:57 +02:00
jakobdyrby 48b60961b5 removed unused code 2014-06-16 12:12:56 +02:00
jakobdyrby 2bea8c5691 back to const 2014-06-16 12:12:56 +02:00
jakobdyrby 32ed0de4f6 Moved and renamed classes to fit standard 2014-06-16 12:12:55 +02:00
jakobdyrby c67f0baf74 Method renaming and zip allowed 2014-06-16 12:12:54 +02:00
jakobdyrby 95ff1a03fb some renaming 2014-06-16 12:12:53 +02:00
jakobdyrby c0b22c4349 renamed a class 2014-06-16 12:12:53 +02:00
jakobdyrby 5d3d2e9331 added comments to IUnpackHelper 2014-06-16 12:12:52 +02:00
jakobdyrby de266351d1 Refactoring to move redundant code and added sanity check for doublet filenames in package 2014-06-16 12:12:52 +02:00
jakobdyrby f8b4caea85 Fixes
now the package does not die hard if data type not found
directories in umb packages are now ignored - not sure if this is th right behaver
small logical errors
2014-06-16 12:12:51 +02:00
jakobdyrby 5691bb0de8 Xml elements case sensitive problems 2014-06-16 12:12:50 +02:00
jakobdyrby fa56413d02 undo rename of argument 2014-06-16 12:12:50 +02:00
jakobdyrby cb5fa8ac13 Removed "double" file 2014-06-16 12:12:49 +02:00
jakobdyrby 531eba6e28 corrected comment 2014-06-16 12:12:49 +02:00
jakobdyrby 4c6ec3ac4b Removed som out commentet code 2014-06-16 12:12:48 +02:00
jakobdyrby 46eaba668c Missed this in last commit 2014-06-16 12:12:48 +02:00
jakobdyrby 889736f63b now loosely testet. 2014-06-16 12:12:47 +02:00
jakobdyrby a03e46339d small layout stuff 2014-06-16 12:12:46 +02:00
jakobdyrby 37fbc3bab4 Now uses new constants 2014-06-16 12:12:46 +02:00
jakobdyrby 9f146753b5 used the right method that returns a single object 2014-06-16 12:12:45 +02:00
jakobdyrby acc6d46b4b splittet into more single resposebility classes 2014-06-16 12:12:45 +02:00
jakobdyrby d57c572116 moved strings to constants 2014-06-16 12:12:44 +02:00
jakobdyrby 70fcd6fab7 more updates 2014-06-16 12:12:44 +02:00
jakobdyrby 45076491cb Added new PackagingService 2014-06-16 12:12:43 +02:00
Henrik Andersen 6019ab13df Updated sortdiaglog frame
Updated the dialog frame to use dynamic height.
2014-06-13 09:28:57 +02:00
Henrik Andersen 83fd0ab7e3 Updated modals.less
Added background color to umb-dialog footer
2014-06-13 09:21:45 +02:00
Jan Skovgaard 4421681538 Fix for U4-4913 2014-06-09 22:38:49 +02:00
Jan Skovgaard 4238b79c67 Fix for U4-4493
Makes it possible to go back if you don't want to customize.
2014-06-09 16:48:27 +02:00
Morten Christensen 02eff33d29 Fixes U4-2640 LocalizationService throws exception when getting dictionary items and adds tests for the LocalizationService 2014-06-08 13:16:32 +02:00
Matthew Sheeley a4b25c3f84 Close user dialog on logout 2014-06-04 15:44:22 -05:00
Stephan 16a1720bf7 Merge remote-tracking branch 'origin/6.2.1' into 6.2.1 2014-05-28 17:11:56 +02:00
Stephan 74806984b3 U4-4931 - fixing & refactoring 2014-05-27 12:17:48 +02:00
Stephan c8c278c7bc Merge remote-tracking branch 'origin/6.2.1' into 6.2.1 2014-05-27 12:17:43 +02:00
Stephan a8e4c3eb9a Merge remote-tracking branch 'origin/6.2.1' into 6.2.1 2014-05-26 18:07:45 +02:00
Stephan 113b866485 U4-4931 - fixing & refactoring 2014-05-26 15:56:29 +02:00
Stephan 6c6ef8d144 Merge remote-tracking branch 'origin/6.2.1' into 6.2.1 2014-05-26 15:03:38 +02:00
Stephan 99250cefbb U4-4931 - fixing & refactoring 2014-05-23 10:46:41 +02:00
Stephan dc651a1c42 Merge remote-tracking branch 'origin/6.2.1' into 6.2.1 2014-05-23 10:46:13 +02:00
Stephan 4e9fd5a223 U4-4931 - fixing & refactoring 2014-05-22 13:43:55 +02:00
Stephan e2c36d7212 Merge remote-tracking branch 'origin/6.2.1' into HEAD 2014-05-22 13:40:58 +02:00
Stephan 695cc07912 Merge remote-tracking branch 'origin/6.2.1' into 6.2.1 2014-05-20 09:32:54 +02:00
Stephan 46f57a3115 U4-4931 - lock contention & potential application lockdown 2014-05-18 19:13:22 +02:00
AndyButland 309e460f07 Merge from 7.1.1 2014-04-13 22:52:41 +02:00
AndyButland c9d1123f0a Removed related links sorting up/down buttons and updated to use drag-drop 2014-02-28 14:42:45 +01:00
AndyButland 07229c4bd2 Up/down buttons for changing the order of related links 2014-01-03 17:08:33 +01:00
342 changed files with 12396 additions and 6148 deletions
+2
View File
@@ -14,12 +14,14 @@ ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %rel
ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise you'd have to run build.cmd twice
SET nuGetFolder=%CD%\..\src\packages\
..\src\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder%
ECHO Removing the belle build folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
ECHO Removing existing built files to make sure everything is clean as a whistle
RMDIR /Q /S _BuildOutput
DEL /F /Q UmbracoCms.*.zip
DEL /F /Q UmbracoCms.*.nupkg
DEL /F /Q webpihash.txt
+9 -7
View File
@@ -15,11 +15,13 @@
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.0,5.0.0)" />
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.30506.0,5.0.0)" />
<dependency id="Microsoft.AspNet.WebApi" version="[4.0.30506,5.0.0)" />
<dependency id="Microsoft.AspNet.WebApi.WebHost" version="[4.0.30506, 5.0.0)" />
<dependency id="Microsoft.AspNet.WebApi.Core" version="[4.0.30506, 5.0.0)" />
<dependency id="Microsoft.AspNet.WebApi.Client" version="[4.0.30506, 5.0.0)" />
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" />
<dependency id="Microsoft.Net.Http" version="[2.0.0, 3.0.0)" />
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" />
<dependency id="Microsoft.Net.Http" version="[2.0.20710.0, 3.0.0)" />
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
<dependency id="HtmlAgilityPack" version="[1.4.6, 2.0.0)" />
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
@@ -29,9 +31,10 @@
<dependency id="ClientDependency" version="[1.7.1.2, 2.0.0)" />
<dependency id="ClientDependency-Mvc" version="[1.7.0.4, 2.0.0)" />
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.2, 7.0.0)" />
<dependency id="ImageProcessor" version="[1.9.0, 2.0.0)" />
<dependency id="ImageProcessor.Web" version="[3.2.3, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.4, 7.0.0)" />
<dependency id="Examine" version="[0.1.57, 1.0.0)" />
<dependency id="ImageProcessor" version="[1.9.5, 2.0.0)" />
<dependency id="ImageProcessor.Web" version="[3.3.0, 4.0.0)" />
</dependencies>
</metadata>
<files>
@@ -41,7 +44,6 @@
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\cms.xml" />
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\controls.dll" />
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\controls.xml" />
<file src="..\_BuildOutput\WebApp\bin\Examine.dll" target="lib\Examine.dll" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\interfaces.dll" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\interfaces.xml" />
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\log4net.dll" />
+1 -1
View File
@@ -1 +1 @@
7.1.5
7.1.7
Binary file not shown.
+1
View File
@@ -77,6 +77,7 @@
<Compile Include="SqlCEUtility.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
@@ -1,218 +1,204 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
namespace Umbraco.Core.Cache
{
internal abstract class DictionaryCacheProviderBase : ICacheProvider
{
protected static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
protected abstract DictionaryCacheWrapper DictionaryCache { get; }
/// <summary>
/// Clears everything in umbraco's runtime cache
/// </summary>
/// <remarks>
/// Does not clear other stuff the user has put in httpruntime.cache!
/// </remarks>
public virtual void ClearAllCache()
{
using (new WriteLock(Locker))
{
var keysToRemove = DictionaryCache.Cast<object>()
.Select(item => new DictionaryItemWrapper(item))
.Where(c => c.Key is string && ((string)c.Key).StartsWith(CacheItemPrefix) && DictionaryCache[c.Key.ToString()] != null)
.Select(c => c.Key)
.ToList();
foreach (var k in keysToRemove)
{
DictionaryCache.Remove(k);
}
}
}
/// <summary>
/// Clears the item in umbraco's runtime cache with the given key
/// </summary>
/// <param name="key">Key</param>
public virtual void ClearCacheItem(string key)
{
using (new WriteLock(Locker))
{
if (DictionaryCache[GetCacheKey(key)] == null) return;
DictionaryCache.Remove(GetCacheKey(key)); ;
}
}
/// <summary>
/// Clears all objects in the System.Web.Cache with the System.Type name as the
/// input parameter. (using [object].GetType())
/// </summary>
/// <param name="typeName">The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument"</param>
public virtual void ClearCacheObjectTypes(string typeName)
{
using (new WriteLock(Locker))
{
var keysToRemove = DictionaryCache
.Cast<object>()
.Select(item => new DictionaryItemWrapper(item))
.Where(c =>
{
var k = c.Key.ToString();
var v = DictionaryCache[k];
return v != null && v.GetType().ToString().InvariantEquals(typeName);
})
.Select(c => c.Key)
.ToList();
foreach (var k in keysToRemove)
DictionaryCache.Remove(k);
}
}
public virtual void ClearCacheObjectTypes<T>()
{
using (new WriteLock(Locker))
{
var typeOfT = typeof(T);
var keysToRemove = DictionaryCache
.Cast<object>()
.Select(item => new DictionaryItemWrapper(item))
.Where(c =>
{
var k = c.Key.ToString();
var v = DictionaryCache[k];
return v != null && v.GetType() == typeOfT;
})
.Select(c => c.Key)
.ToList();
foreach (var k in keysToRemove)
DictionaryCache.Remove(k);
}
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
using (new WriteLock(Locker))
{
var typeOfT = typeof(T);
var keysToRemove = DictionaryCache
.Cast<object>()
.Select(item => new DictionaryItemWrapper(item))
.Where(c =>
{
var k = c.Key.ToString();
var v = DictionaryCache[k];
return v != null && v.GetType() == typeOfT && predicate(k, (T)v);
})
.Select(c => c.Key)
.ToList();
foreach (var k in keysToRemove)
DictionaryCache.Remove(k);
}
}
/// <summary>
/// Clears all cache items that starts with the key passed.
/// </summary>
/// <param name="keyStartsWith">The start of the key</param>
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
var keysToRemove = DictionaryCache.Cast<object>()
.Select(item => new DictionaryItemWrapper(item))
.Where(c => c.Key is string && ((string)c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)))
.Select(c => c.Key)
.ToList();
foreach (var k in keysToRemove)
{
DictionaryCache.Remove(k);
}
}
/// <summary>
/// Clears all cache items that have a key that matches the regular expression
/// </summary>
/// <param name="regexString"></param>
public virtual void ClearCacheByKeyExpression(string regexString)
{
var keysToRemove = new List<object>();
foreach (var item in DictionaryCache)
{
var c = new DictionaryItemWrapper(item);
var s = c.Key as string;
if (s != null)
{
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
if (Regex.IsMatch(withoutPrefix, regexString))
{
keysToRemove.Add(c.Key);
}
}
}
foreach (var k in keysToRemove)
{
DictionaryCache.Remove(k);
}
}
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return (from object item in DictionaryCache
select new DictionaryItemWrapper(item)
into c
where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))
select c.Value).ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
var found = new List<object>();
foreach (var item in DictionaryCache)
{
var c = new DictionaryItemWrapper(item);
var s = c.Key as string;
if (s != null)
{
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
if (Regex.IsMatch(withoutPrefix, regexString))
{
found.Add(c.Value);
}
}
}
return found;
}
/// <summary>
/// Returns a cache item by key, does not update the cache if it isn't there.
/// </summary>
/// <param name="cacheKey"></param>
/// <returns></returns>
public virtual object GetCacheItem(string cacheKey)
{
var result = DictionaryCache.Get(GetCacheKey(cacheKey));
return result;
}
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
/// <summary>
/// We prefix all cache keys with this so that we know which ones this class has created when
/// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create.
/// </summary>
// prefix cache keys so we know which one are ours
protected const string CacheItemPrefix = "umbrtmche";
// an object that represent a value that has not been created yet
protected readonly object ValueNotCreated = new object();
// manupulate the underlying cache entries
// these *must* be called from within the appropriate locks
// and use the full prefixed cache keys
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
protected abstract void RemoveEntry(string key);
protected abstract object GetEntry(string key);
// read-write lock the underlying cache
protected abstract IDisposable ReadLock { get; }
protected abstract IDisposable WriteLock { get; }
protected string GetCacheKey(string key)
{
return string.Format("{0}-{1}", CacheItemPrefix, key);
}
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
try
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
return lazy.Value;
}
catch
{
return null;
}
}
#region Clear
public virtual void ClearAllCache()
{
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheItem(string key)
{
var cacheKey = GetCacheKey(key);
using (WriteLock)
{
RemoveEntry(cacheKey);
}
}
public virtual void ClearCacheObjectTypes(string typeName)
{
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType().ToString().InvariantEquals(typeName);
})
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType() == typeOfT;
})
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var plen = CacheItemPrefix.Length + 1;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
return value.GetType() == typeOfT
// run predicate on the 'public key' part only, ie without prefix
&& predicate(((string)x.Key).Substring(plen), (T)value);
}))
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheByKeyExpression(string regexString)
{
var plen = CacheItemPrefix.Length + 1;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray())
RemoveEntry((string) entry.Key);
}
}
#endregion
#region Get
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
using (ReadLock)
{
entries = GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
const string prefix = CacheItemPrefix + "-";
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
using (ReadLock)
{
entries = GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
public virtual object GetCacheItem(string cacheKey)
{
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
using (ReadLock)
{
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
}
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
#endregion
}
}
@@ -1,45 +0,0 @@
using System;
using System.Collections;
namespace Umbraco.Core.Cache
{
internal class DictionaryCacheWrapper : IEnumerable
{
private readonly IEnumerable _inner;
private readonly Func<object, object> _get;
private readonly Action<object> _remove;
public DictionaryCacheWrapper(
IEnumerable inner,
Func<object, object> get,
Action<object> remove)
{
_inner = inner;
_get = get;
_remove = remove;
}
public object this[object key]
{
get
{
return Get(key);
}
}
public object Get(object key)
{
return _get(key);
}
public void Remove(object key)
{
_remove(key);
}
public IEnumerator GetEnumerator()
{
return _inner.GetEnumerator();
}
}
}
@@ -1,14 +0,0 @@
namespace Umbraco.Core.Cache
{
internal class DictionaryItemWrapper
{
public DictionaryItemWrapper(dynamic item)
{
Key = item.Key;
Value = item.Value;
}
public object Key { get; private set; }
public object Value { get; private set; }
}
}
@@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
namespace Umbraco.Core.Cache
@@ -12,36 +11,115 @@ namespace Umbraco.Core.Cache
/// </summary>
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
{
private readonly Func<HttpContextBase> _context;
// context provider
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
// that is initialized with a method that returns the "current" context.
// NOTE
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
// so replace it with _context1 and _context2 below + a way to get context.Items.
//private readonly Func<HttpContextBase> _context;
public HttpRequestCacheProvider(HttpContext context)
// NOTE
// and then in almost 100% cases _context2 will be () => HttpContext.Current
// so why not bring that logic in here and fallback on to HttpContext.Current when
// _context1 is null?
//private readonly HttpContextBase _context1;
//private readonly Func<HttpContext> _context2;
private readonly HttpContextBase _context;
private IDictionary ContextItems
{
_context = () => new HttpContextWrapper(context);
//get { return _context1 != null ? _context1.Items : _context2().Items; }
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
}
public HttpRequestCacheProvider(Func<HttpContextBase> context)
// for unit tests
public HttpRequestCacheProvider(HttpContextBase context)
{
_context = context;
}
protected override DictionaryCacheWrapper DictionaryCache
// main constructor
// will use HttpContext.Current
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
{
//_context2 = context;
}
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
return ContextItems.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
ContextItems.Remove(key);
}
protected override object GetEntry(string key)
{
return ContextItems[key];
}
#region Lock
protected override IDisposable ReadLock
{
// there's no difference between ReadLock and WriteLock here
get { return WriteLock; }
}
protected override IDisposable WriteLock
{
// NOTE
// could think about just overriding base.Locker to return a different
// object but then we'd create a ReaderWriterLockSlim per request,
// which is less efficient than just using a basic monitor lock.
get
{
var ctx = _context();
return new DictionaryCacheWrapper(
ctx.Items,
o => ctx.Items[o],
o => ctx.Items.Remove(o));
return new MonitorLock(ContextItems.SyncRoot);
}
}
#endregion
#region Get
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
var ctx = _context();
var ck = GetCacheKey(cacheKey);
return ctx.Items[ck] ?? (ctx.Items[ck] = getCacheItem());
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
using (WriteLock)
{
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = new Lazy<object>(getCacheItem);
ContextItems[cacheKey] = result;
}
}
// this may throw if getCacheItem throws, but this is the only place where
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
// and pretend exceptions were never inserted into cache to begin with.
return result.Value;
}
#endregion
#region Insert
#endregion
}
}
@@ -1,10 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Caching;
using Umbraco.Core.Logging;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
@@ -14,48 +13,52 @@ namespace Umbraco.Core.Cache
/// </summary>
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
{
// locker object that supports upgradeable read locking
// does not need to support recursion if we implement the cache correctly and ensure
// that methods cannot be reentrant, ie we do NOT create values while holding a lock.
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private readonly System.Web.Caching.Cache _cache;
private readonly DictionaryCacheWrapper _wrapper;
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
{
_cache = cache;
_wrapper = new DictionaryCacheWrapper(_cache, s => _cache.Get(s.ToString()), o => _cache.Remove(o.ToString()));
}
protected override DictionaryCacheWrapper DictionaryCache
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
get { return _wrapper; }
const string prefix = CacheItemPrefix + "-";
return _cache.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string) x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
_cache.Remove(key);
}
protected override object GetEntry(string key)
{
return _cache.Get(key);
}
#region Lock
protected override IDisposable ReadLock
{
get { return new ReadLock(_locker); }
}
protected override IDisposable WriteLock
{
get { return new WriteLock(_locker); }
}
#endregion
#region Get
/// <summary>
/// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate
/// </summary>
public override void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
try
{
lock (Locker)
{
foreach (DictionaryEntry c in _cache)
{
var key = c.Key.ToString();
if (_cache[key] != null
&& _cache[key] is T
&& predicate(key, (T)_cache[key]))
{
_cache.Remove(c.Key.ToString());
}
}
}
}
catch (Exception e)
{
LogHelper.Error<CacheHelper>("Cache clearing error", e);
}
}
/// <summary>
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
/// </summary>
/// <param name="cacheKey"></param>
@@ -81,25 +84,64 @@ namespace Umbraco.Core.Cache
{
cacheKey = GetCacheKey(cacheKey);
using (var lck = new UpgradeableReadLock(Locker))
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
// nasty performance issues.
// So.... we insert a Lazy<object> in the cache while holding the global application lock, and then rely
// on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global
// application lock has been released.
// NOTE
// The Lazy value creation may produce a null value.
// Must make sure (for backward compatibility) that we pretend they are not in the cache.
// So if we find an entry in the cache that already has its value created and is null,
// pretend it was not there. If value is not already created, wait... and return null, that's
// what prior code did.
// NOTE
// The Lazy value creation may throw.
// So... the null value _will_ be in the cache but never returned
Lazy<object> result;
// Fast!
// Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other
// threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all
// reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of
// having to re-lock in case there's no value. Would need to benchmark to figure out whether
// it's worth it, though...
using (new ReadLock(_locker))
{
var result = DictionaryCache.Get(cacheKey);
if (result == null)
{
lck.UpgradeToWriteLock();
result = getCacheItem();
if (result != null)
{
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
return result;
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
}
var value = result == null ? null : GetSafeLazyValue(result);
if (value != null) return value;
using (var lck = new UpgradeableReadLock(_locker))
{
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = new Lazy<object>(getCacheItem);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
lck.UpgradeToWriteLock();
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
// this may throw if getCacheItem throws, but this is the only place where
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
// and pretend exceptions were never inserted into cache to begin with.
return result.Value;
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
@@ -112,6 +154,10 @@ namespace Umbraco.Core.Cache
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
#region Insert
/// <summary>
/// This overload is here for legacy purposes
/// </summary>
@@ -124,15 +170,22 @@ namespace Umbraco.Core.Cache
/// <param name="dependency"></param>
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
{
var result = getCacheItem();
if (result == null) return;
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = new Lazy<object>(getCacheItem);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return; // do not store null values (backward compat)
cacheKey = GetCacheKey(cacheKey);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
using (new WriteLock(_locker))
{
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
@@ -144,5 +197,7 @@ namespace Umbraco.Core.Cache
}
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
}
}
@@ -16,17 +16,37 @@ namespace Umbraco.Core.Cache
/// </summary>
internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
{
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
internal ObjectCache MemoryCache;
// an object that represent a value that has not been created yet
protected readonly object ValueNotCreated = new object();
public ObjectCacheRuntimeCacheProvider()
{
MemoryCache = new MemoryCache("in-memory");
}
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
try
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
return lazy.Value;
}
catch
{
return null;
}
}
#region Clear
public virtual void ClearAllCache()
{
using (new WriteLock(Locker))
using (new WriteLock(_locker))
{
MemoryCache.DisposeIfDisposable();
MemoryCache = new MemoryCache("in-memory");
@@ -35,7 +55,7 @@ namespace Umbraco.Core.Cache
public virtual void ClearCacheItem(string key)
{
using (new WriteLock(Locker))
using (new WriteLock(_locker))
{
if (MemoryCache[key] == null) return;
MemoryCache.Remove(key);
@@ -44,131 +64,179 @@ namespace Umbraco.Core.Cache
public virtual void ClearCacheObjectTypes(string typeName)
{
using (new WriteLock(Locker))
using (new WriteLock(_locker))
{
var keysToRemove = MemoryCache
.Where(c => c.Value != null && c.Value.GetType().ToString().InvariantEquals(typeName))
.Select(c => c.Key)
.ToArray();
foreach (var k in keysToRemove)
MemoryCache.Remove(k);
foreach (var key in MemoryCache
.Where(x =>
{
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType().ToString().InvariantEquals(typeName);
})
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
}
}
public virtual void ClearCacheObjectTypes<T>()
{
using (new WriteLock(Locker))
using (new WriteLock(_locker))
{
var typeOfT = typeof (T);
var keysToRemove = MemoryCache
.Where(c => c.Value != null && c.Value.GetType() == typeOfT)
.Select(c => c.Key)
.ToArray();
foreach (var k in keysToRemove)
MemoryCache.Remove(k);
foreach (var key in MemoryCache
.Where(x =>
{
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType() == typeOfT;
})
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
}
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
using (new WriteLock(Locker))
using (new WriteLock(_locker))
{
var typeOfT = typeof(T);
var keysToRemove = MemoryCache
.Where(c => c.Value != null && c.Value.GetType() == typeOfT && predicate(c.Key, (T)c.Value))
.Select(c => c.Key)
.ToArray();
foreach (var k in keysToRemove)
MemoryCache.Remove(k);
foreach (var key in MemoryCache
.Where(x =>
{
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
return value.GetType() == typeOfT
&& predicate(x.Key, (T) value);
})
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
}
}
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
using (new WriteLock(Locker))
using (new WriteLock(_locker))
{
var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList();
foreach (var k in keysToRemove)
{
MemoryCache.Remove(k);
}
foreach (var key in MemoryCache
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
}
}
public virtual void ClearCacheByKeyExpression(string regexString)
{
using (new WriteLock(Locker))
using (new WriteLock(_locker))
{
var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList();
foreach (var k in keysToRemove)
{
MemoryCache.Remove(k);
}
foreach (var key in MemoryCache
.Where(x => Regex.IsMatch(x.Key, regexString))
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
}
}
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
#endregion
#region Get
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return (from c in MemoryCache
where c.Key.InvariantStartsWith(keyStartsWith)
select c.Value).ToList();
KeyValuePair<string, object>[] entries;
using (new ReadLock(_locker))
{
entries = MemoryCache
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return (from c in MemoryCache
where Regex.IsMatch(c.Key, regexString)
select c.Value).ToList();
KeyValuePair<string, object>[] entries;
using (new ReadLock(_locker))
{
entries = MemoryCache
.Where(x => Regex.IsMatch(x.Key, regexString))
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
public virtual object GetCacheItem(string cacheKey)
public object GetCacheItem(string cacheKey)
{
var result = MemoryCache.Get(cacheKey);
return result;
Lazy<object> result;
using (new ReadLock(_locker))
{
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
}
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return GetCacheItem(cacheKey, getCacheItem, null);
}
public object GetCacheItem(
string cacheKey,
Func<object> getCacheItem,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal,CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
using (var lck = new UpgradeableReadLock(Locker))
{
var result = MemoryCache.Get(cacheKey);
if (result == null)
{
lck.UpgradeToWriteLock();
// see notes in HttpRuntimeCacheProvider
result = getCacheItem();
if (result != null)
{
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
MemoryCache.Set(cacheKey, result, policy);
}
Lazy<object> result;
using (var lck = new UpgradeableReadLock(_locker))
{
result = MemoryCache.Get(cacheKey) as Lazy<object>;
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = new Lazy<object>(getCacheItem);
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
lck.UpgradeToWriteLock();
MemoryCache.Set(cacheKey, result, policy);
}
return result;
}
return result.Value;
}
#endregion
#region Insert
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
object result = getCacheItem();
if (result != null)
{
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
MemoryCache.Set(cacheKey, result, policy);
}
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = new Lazy<object>(getCacheItem);
var value = result.Value; // force evaluation now
if (value == null) return; // do not store null values (backward compat)
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
MemoryCache.Set(cacheKey, result, policy);
}
#endregion
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
+2 -2
View File
@@ -44,7 +44,7 @@ namespace Umbraco.Core
: this(
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
new StaticCacheProvider(),
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
new HttpRequestCacheProvider())
{
}
@@ -56,7 +56,7 @@ namespace Umbraco.Core
: this(
new HttpRuntimeCacheProvider(cache),
new StaticCacheProvider(),
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
new HttpRequestCacheProvider())
{
}
@@ -5,5 +5,8 @@
string ForcePortnumber { get; }
string ForceProtocol { get; }
string ServerAddress { get; }
string AppId { get; }
string ServerName { get; }
}
}
@@ -26,5 +26,24 @@
{
get { return Value; }
}
public string AppId
{
get
{
return RawXml.Attribute("appId") == null
? null
: RawXml.Attribute("appId").Value;
}
}
public string ServerName
{
get
{
return RawXml.Attribute("serverName") == null
? null
: RawXml.Attribute("serverName").Value;
}
}
}
}
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.1.5");
private static readonly Version Version = new Version("7.1.7");
/// <summary>
/// Gets the current version of Umbraco.
+16
View File
@@ -279,6 +279,22 @@ namespace Umbraco.Core
/// </summary>
public const string AltTemplate = "altTemplate";
}
/// <summary>
/// Defines the alias identifiers for Umbraco relation types.
/// </summary>
public static class RelationTypes
{
/// <summary>
/// ContentType name for default relation type "Relate Document On Copy".
/// </summary>
public const string RelateDocumentOnCopyName = "Relate Document On Copy";
/// <summary>
/// ContentType alias for default relation type "Relate Document On Copy".
/// </summary>
public const string RelateDocumentOnCopyAlias = "relateDocumentOnCopy";
}
}
}
}
+54 -50
View File
@@ -1,51 +1,55 @@
namespace Umbraco.Core
{
public static partial class Constants
{
/// <summary>
/// Defines the constants used for Umbraco packages in the package.config xml
/// </summary>
public static class Packaging
{
public const string UmbPackageNodeName = "umbPackage";
public const string DataTypesNodeName = "DataTypes";
public const string PackageXmlFileName = "package.xml";
public const string UmbracoPackageExtention = ".umb";
public const string DataTypeNodeName = "DataType";
public const string LanguagesNodeName = "Languages";
public const string FilesNodeName = "Files";
public const string StylesheetsNodeName = "Stylesheets";
public const string TemplatesNodeName = "Templates";
public const string OrgnameNodeName = "orgName";
public const string NameNodeName = "Name";
public const string TemplateNodeName = "Template";
public const string AliasNodeName = "Alias";
public const string DictionaryitemsNodeName = "DictionaryItems";
public const string MacrosNodeName = "macros";
public const string DocumentSetNodeName = "DocumentSet";
public const string DocumentTypesNodeName = "DocumentTypes";
public const string DocumentTypeNodeName = "DocumentType";
public const string FileNodeName = "file";
public const string OrgPathNodeName = "orgPath";
public const string GuidNodeName = "guid";
public const string StylesheetNodeName = "styleSheet";
public const string MacroNodeName = "macro";
public const string InfoNodeName = "info";
public const string PackageRequirementsMajorXpath = "/package/requirements/major";
public const string PackageRequirementsMinorXpath = "/package/requirements/minor";
public const string PackageRequirementsPatchXpath = "/package/requirements/patch";
public const string PackageNameXpath = "/package/name";
public const string PackageVersionXpath = "/package/version";
public const string PackageUrlXpath = "/package/url";
public const string PackageLicenseXpath = "/package/license";
public const string AuthorNameXpath = "/author/name";
public const string AuthorWebsiteXpath = "/author/website";
public const string ReadmeXpath = "/readme";
public const string ControlNodeName = "control";
public const string ActionNodeName = "Action";
public const string ActionsNodeName = "Actions";
public const string UndoNodeAttribute = "undo";
public const string RunatNodeAttribute = "runat";
}
}
namespace Umbraco.Core
{
public static partial class Constants
{
/// <summary>
/// Defines the constants used for Umbraco packages in the package.config xml
/// </summary>
public static class Packaging
{
public const string UmbPackageNodeName = "umbPackage";
public const string DataTypesNodeName = "DataTypes";
public const string PackageXmlFileName = "package.xml";
public const string UmbracoPackageExtention = ".umb";
public const string DataTypeNodeName = "DataType";
public const string LanguagesNodeName = "Languages";
public const string FilesNodeName = "files";
public const string StylesheetsNodeName = "Stylesheets";
public const string TemplatesNodeName = "Templates";
public const string NameNodeName = "Name";
public const string TemplateNodeName = "Template";
public const string AliasNodeNameSmall = "alias";
public const string AliasNodeNameCapital = "Alias";
public const string DictionaryItemsNodeName = "DictionaryItems";
public const string DictionaryItemNodeName = "DictionaryItem";
public const string MacrosNodeName = "Macros";
public const string DocumentsNodeName = "Documents";
public const string DocumentSetNodeName = "DocumentSet";
public const string DocumentTypesNodeName = "DocumentTypes";
public const string DocumentTypeNodeName = "DocumentType";
public const string FileNodeName = "file";
public const string OrgNameNodeName = "orgName";
public const string OrgPathNodeName = "orgPath";
public const string GuidNodeName = "guid";
public const string StylesheetNodeName = "styleSheet";
public const string MacroNodeName = "macro";
public const string InfoNodeName = "info";
public const string PackageRequirementsMajorXpath = "./package/requirements/major";
public const string PackageRequirementsMinorXpath = "./package/requirements/minor";
public const string PackageRequirementsPatchXpath = "./package/requirements/patch";
public const string PackageNameXpath = "./package/name";
public const string PackageVersionXpath = "./package/version";
public const string PackageUrlXpath = "./package/url";
public const string PackageLicenseXpath = "./package/license";
public const string PackageLicenseXpathUrlAttribute = "url";
public const string AuthorNameXpath = "./author/name";
public const string AuthorWebsiteXpath = "./author/website";
public const string ReadmeXpath = "./readme";
public const string ControlNodeName = "control";
public const string ActionNodeName = "Action";
public const string ActionsNodeName = "Actions";
public const string UndoNodeAttribute = "undo";
public const string RunatNodeAttribute = "runat";
}
}
}
+40 -28
View File
@@ -1,35 +1,47 @@
namespace Umbraco.Core.Events
{
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
{
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId) : base(original, canCancel)
{
Copy = copy;
ParentId = parentId;
}
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
{
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId)
: base(original, canCancel)
{
Copy = copy;
ParentId = parentId;
}
public CopyEventArgs(TEntity eventObject, TEntity copy, int parentId) : base(eventObject)
{
Copy = copy;
ParentId = parentId;
}
public CopyEventArgs(TEntity eventObject, TEntity copy, int parentId)
: base(eventObject)
{
Copy = copy;
ParentId = parentId;
}
/// <summary>
/// The copied entity
/// </summary>
public TEntity Copy { get; set; }
public CopyEventArgs(TEntity eventObject, TEntity copy, bool canCancel, int parentId, bool relateToOriginal)
: base(eventObject, canCancel)
{
Copy = copy;
ParentId = parentId;
RelateToOriginal = relateToOriginal;
}
/// <summary>
/// The original entity
/// </summary>
public TEntity Original
{
get { return EventObject; }
}
/// <summary>
/// The copied entity
/// </summary>
public TEntity Copy { get; set; }
/// <summary>
/// Gets or Sets the Id of the objects new parent.
/// </summary>
public int ParentId { get; private set; }
}
/// <summary>
/// The original entity
/// </summary>
public TEntity Original
{
get { return EventObject; }
}
/// <summary>
/// Gets or Sets the Id of the objects new parent.
/// </summary>
public int ParentId { get; private set; }
public bool RelateToOriginal { get; set; }
}
}
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Umbraco.Core.Packaging.Models;
namespace Umbraco.Core.Events
{
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
{
private readonly MetaData _packageMetaData;
public ImportPackageEventArgs(TEntity eventObject, bool canCancel)
: base(new[] { eventObject }, canCancel)
{
}
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
: base(new[] { eventObject })
{
_packageMetaData = packageMetaData;
}
public MetaData PackageMetaData
{
get { return _packageMetaData; }
}
}
}
@@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Core.Events
{
public class MigrationEventArgs : CancellableObjectEventArgs<IEnumerable<IMigration>>
public class MigrationEventArgs : CancellableObjectEventArgs<IList<IMigration>>
{
/// <summary>
/// Constructor accepting multiple migrations that are used in the migration runner
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Events
/// <param name="targetVersion"></param>
/// <param name="canCancel"></param>
/// <param name="configuredVersion"></param>
public MigrationEventArgs(IEnumerable<IMigration> eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
: base(eventObject, canCancel)
{
ConfiguredVersion = configuredVersion;
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Events
/// <param name="targetVersion"></param>
/// <param name="canCancel"></param>
/// <param name="configuredVersion"></param>
internal MigrationEventArgs(IEnumerable<IMigration> eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel)
internal MigrationEventArgs(IList<IMigration> eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel)
: base(eventObject, canCancel)
{
MigrationContext = migrationContext;
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Events
/// <param name="eventObject"></param>
/// <param name="configuredVersion"></param>
/// <param name="targetVersion"></param>
public MigrationEventArgs(IEnumerable<IMigration> eventObject, Version configuredVersion, Version targetVersion)
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion)
: base(eventObject)
{
ConfiguredVersion = configuredVersion;
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Events
/// <summary>
/// Returns all migrations that were used in the migration runner
/// </summary>
public IEnumerable<IMigration> Migrations
public IList<IMigration> Migrations
{
get { return EventObject; }
}
+6
View File
@@ -34,6 +34,12 @@ namespace Umbraco.Core
AddInt(d.GetHashCode());
}
internal void AddString(string s)
{
if (s != null)
AddInt((StringComparer.InvariantCulture).GetHashCode(s));
}
internal void AddCaseInsensitiveString(string s)
{
if (s != null)
+4 -2
View File
@@ -1,6 +1,5 @@
using System;
using System.IO;
using Umbraco.Core.CodeAnnotations;
namespace Umbraco.Core.IO
{
@@ -19,7 +18,10 @@ namespace Umbraco.Core.IO
public static void CopyFile(this IFileSystem fs, string path, string newPath)
{
fs.AddFile(newPath, fs.OpenFile(path));
using (var stream = fs.OpenFile(path))
{
fs.AddFile(newPath, stream);
}
}
public static string GetExtension(this IFileSystem fs, string path)
-1
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Umbraco.Core.CodeAnnotations;
namespace Umbraco.Core.IO
{
-1
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Umbraco.Core.CodeAnnotations;
namespace Umbraco.Core.IO
{
+3 -5
View File
@@ -3,10 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using Umbraco.Core.CodeAnnotations;
using Umbraco.Core.Logging;
using Umbraco.Core.Publishing;
namespace Umbraco.Core.IO
{
@@ -93,8 +90,9 @@ namespace Umbraco.Core.IO
public void AddFile(string path, Stream stream, bool overrideIfExists)
{
if (FileExists(path) && !overrideIfExists) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
var exists = FileExists(path);
if (exists && overrideIfExists == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
EnsureDirectory(Path.GetDirectoryName(path));
if (stream.CanSeek)
+11 -12
View File
@@ -154,19 +154,18 @@ namespace Umbraco.Core.IO
EnsureFileSupportsResizing();
using (var fs = _fs.OpenFile(Path))
using (var image = Image.FromStream(fs))
{
using (var image = Image.FromStream(fs))
{
var fileWidth = image.Width;
var fileHeight = image.Height;
_size = new Size(fileWidth, fileHeight);
}
var fileWidth = image.Width;
var fileHeight = image.Height;
_size = new Size(fileWidth, fileHeight);
}
}
else
{
_size = new Size(-1, -1);
}
}
}
return _size.Value;
}
@@ -179,7 +178,7 @@ namespace Umbraco.Core.IO
var fileNameThumb = DoResize(width, height, -1, string.Empty);
return _fs.GetUrl(fileNameThumb);
return _fs.GetUrl(fileNameThumb);
}
return string.Empty;
}
@@ -192,7 +191,7 @@ namespace Umbraco.Core.IO
var fileNameThumb = DoResize(-1, -1, maxWidthHeight, fileNameAddition);
return _fs.GetUrl(fileNameThumb);
return _fs.GetUrl(fileNameThumb);
}
return string.Empty;
}
@@ -206,14 +205,14 @@ namespace Umbraco.Core.IO
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
: string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
var thumbnail = maxWidthHeight == -1
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
: ImageHelper.GenerateThumbnail(image, maxWidthHeight, fileNameThumb, Extension, _fs);
return thumbnail.FileName;
}
}
}
}
private void EnsureFileSupportsResizing()
@@ -222,6 +221,6 @@ namespace Umbraco.Core.IO
throw new InvalidOperationException(string.Format("The file {0} is not an image, so can't get dimensions", Filename));
}
}
}
+17 -3
View File
@@ -8,6 +8,8 @@ namespace Umbraco.Core.Manifest
internal class ManifestWatcher : DisposableObject
{
private readonly List<FileSystemWatcher> _fws = new List<FileSystemWatcher>();
private static volatile bool _isRestarting = false;
private static readonly object Locker = new object();
public void Start(params string[] packageFolders)
{
@@ -32,9 +34,21 @@ namespace Umbraco.Core.Manifest
{
if (e.Name.InvariantContains("package.manifest"))
{
LogHelper.Info<ManifestWatcher>("manifest has changed, app pool is restarting (" + e.FullPath + ")");
HttpRuntime.UnloadAppDomain();
Dispose();
//Ensure the app is not restarted multiple times for multiple saving during the same app domain execution
if (_isRestarting == false)
{
lock (Locker)
{
if (_isRestarting == false)
{
_isRestarting = true;
LogHelper.Info<ManifestWatcher>("manifest has changed, app pool is restarting (" + e.FullPath + ")");
HttpRuntime.UnloadAppDomain();
Dispose();
}
}
}
}
}
+10
View File
@@ -165,6 +165,16 @@ namespace Umbraco.Core.Media
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
// Save the new image using the dimensions of the image
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
using (var ms = new MemoryStream())
{
bp.Save(ms, codec, ep);
ms.Seek(0, 0);
fs.AddFile(predictableThumbnailName, ms);
}
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh));
using (var ms = new MemoryStream())
{
+28 -4
View File
@@ -240,6 +240,28 @@ namespace Umbraco.Core.Models
return content.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Contains(recycleBinId.ToInvariantString());
}
/// <summary>
/// Removes characters that are not valide XML characters from all entity properties
/// of type string. See: http://stackoverflow.com/a/961504/5018
/// </summary>
/// <returns></returns>
/// <remarks>
/// If this is not done then the xml cache can get corrupt and it will throw YSODs upon reading it.
/// </remarks>
/// <param name="entity"></param>
public static void SanitizeEntityPropertiesForXmlStorage(this IContentBase entity)
{
entity.Name = entity.Name.ToValidXmlString();
foreach (var property in entity.Properties)
{
if (property.Value is string)
{
var value = (string)property.Value;
property.Value = value.ToValidXmlString();
}
}
}
/// <summary>
/// Checks if the IContentBase has children
@@ -590,6 +612,12 @@ namespace Umbraco.Core.Models
{
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
}
property.SetTags(storageType, propertyTypeAlias, tags, replaceTags, tagGroup);
}
internal static void SetTags(this Property property, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
{
if (property == null) throw new ArgumentNullException("property");
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
@@ -728,10 +756,6 @@ namespace Umbraco.Core.Models
{
return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member);
}
#endregion
}
}
+18 -10
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -27,6 +28,11 @@ namespace Umbraco.Core.Models
public static class DeepCloneHelper
{
/// <summary>
/// Used to avoid constant reflection (perf)
/// </summary>
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
/// <summary>
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
/// </summary>
@@ -43,16 +49,18 @@ namespace Umbraco.Core.Models
throw new InvalidOperationException("Both the input and output types must be the same");
}
var refProperties = inputType.GetProperties()
.Where(x =>
//is not attributed with the ignore clone attribute
x.GetCustomAttribute<DoNotCloneAttribute>() == null
//reference type but not string
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
//settable
&& x.CanWrite
//non-indexed
&& x.GetIndexParameters().Any() == false);
var refProperties = PropCache.GetOrAdd(inputType, type =>
inputType.GetProperties()
.Where(x =>
//is not attributed with the ignore clone attribute
x.GetCustomAttribute<DoNotCloneAttribute>() == null
//reference type but not string
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
//settable
&& x.CanWrite
//non-indexed
&& x.GetIndexParameters().Any() == false)
.ToArray());
foreach (var propertyInfo in refProperties)
{
+3 -6
View File
@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
@@ -23,5 +20,5 @@ namespace Umbraco.Core.Models
var dirty = (IRememberBeingDirty)entity;
return dirty.WasPropertyDirty("Id");
}
}
}
}
}
+5
View File
@@ -3,6 +3,9 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a Tag, which is composed of a Text, Group and NodeCount property.
/// </summary>
public interface ITag : IAggregateRoot
{
[DataMember]
@@ -11,6 +14,8 @@ namespace Umbraco.Core.Models
[DataMember]
string Group { get; set; }
int NodeCount { get; }
//TODO: enable this at some stage
//int ParentId { get; set; }
}
@@ -1,4 +1,6 @@
namespace Umbraco.Core.Models.Membership
using System.Collections;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents a user -> entity permission
@@ -0,0 +1,59 @@
using System.Collections.Generic;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents an entity -> user & permission key value pair collection
/// </summary>
public class EntityPermissionSet
{
/// <summary>
/// The entity id with permissions assigned
/// </summary>
public int EntityId { get; private set; }
/// <summary>
/// The key/value pairs of user id & single permission
/// </summary>
public IEnumerable<UserPermission> UserPermissionsSet { get; private set; }
public EntityPermissionSet(int entityId, IEnumerable<UserPermission> userPermissionsSet)
{
EntityId = entityId;
UserPermissionsSet = userPermissionsSet;
}
public class UserPermission
{
public UserPermission(int userId, string permission)
{
UserId = userId;
Permission = permission;
}
public int UserId { get; private set; }
public string Permission { get; private set; }
protected bool Equals(UserPermission other)
{
return UserId == other.UserId && string.Equals(Permission, other.Permission);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UserPermission) obj);
}
public override int GetHashCode()
{
unchecked
{
return (UserId*397) ^ Permission.GetHashCode();
}
}
}
}
}
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Defines the base contract for <see cref="IMember"/> and <see cref="IUser"/>
/// </summary>
public interface IMembershipUser : IAggregateRoot
{
object ProviderUserKey { get; set; }
+48 -9
View File
@@ -1,27 +1,55 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents the Type for a Backoffice User
/// </summary>
/// <remarks>
/// Should be internal until a proper user/membership implementation
/// is part of the roadmap.
/// </remarks>
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
internal class UserType : Entity, IUserType
{
[DataMember]
public string Alias { get; set; }
private string _alias;
private string _name;
private IEnumerable<string> _permissions;
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Name);
private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Alias);
private static readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo<UserType, IEnumerable<string>>(x => x.Permissions);
[DataMember]
public string Name { get; set; }
public string Alias
{
get { return _alias; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_alias = value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
return _alias;
}, _alias, AliasSelector);
}
}
[DataMember]
public string Name
{
get { return _name; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_name = value;
return _name;
}, _name, NameSelector);
}
}
/// <summary>
/// The set of default permissions for the user type
@@ -30,6 +58,17 @@ namespace Umbraco.Core.Models.Membership
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
[DataMember]
public IEnumerable<string> Permissions { get; set; }
public IEnumerable<string> Permissions
{
get { return _permissions; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_permissions = value;
return _permissions;
}, _permissions, PermissionsSelector);
}
}
}
}
+74
View File
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Umbraco.Core.IO;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a Partial View file
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
internal class PartialView : File
{
private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled);
public PartialView(string path)
: base(path)
{
base.Path = path;
}
/// <summary>
/// Boolean indicating whether the file could be validated
/// </summary>
/// <returns>True if file is valid, otherwise false</returns>
public override bool IsValid()
{
//TODO: Validate using the macro engine
//var engine = MacroEngineFactory.GetEngine(PartialViewMacroEngine.EngineName);
//engine.Validate(...)
var validatePath = IOHelper.ValidateEditPath(IOHelper.MapPath(Path), BasePath);
var verifyFileExtension = IOHelper.VerifyFileExtension(Path, new List<string> { "cshtml" });
return validatePath && verifyFileExtension;
}
public string OldFileName { get; set; }
public string FileName { get; set; }
public string SnippetName { get; set; }
public bool CreateMacro { get; set; }
public string CodeHeader { get; set; }
public string ParentFolderName { get; set; }
public string EditViewFile { get; set; }
public string BasePath { get; set; }
public string ReturnUrl { get; set; }
internal Regex HeaderMatch
{
get { return _headerMatch; }
}
internal Attempt<string> TryGetSnippetPath(string fileName)
{
var partialViewsFileSystem = new PhysicalFileSystem(BasePath);
var snippetPath = IOHelper.MapPath(string.Format("{0}/PartialViewMacros/Templates/{1}", SystemDirectories.Umbraco, fileName));
return partialViewsFileSystem.FileExists(snippetPath)
? Attempt<string>.Succeed(snippetPath)
: Attempt<string>.Fail();
}
}
}
@@ -43,6 +43,11 @@ namespace Umbraco.Core.Models.PublishedContent
internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable<IPublishedContent> contentSet)
{
// first unwrap content down to the lowest possible level, ie either the deepest inner
// IPublishedContent or the first extended that has added properties. this is to avoid
// nesting extended objects as much as possible, so we try to re-extend that lowest
// object.
var wrapped = content as PublishedContentExtended;
while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false)
wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended;
@@ -51,14 +56,38 @@ namespace Umbraco.Core.Models.PublishedContent
// a model, and then that model has to inherit from PublishedContentExtended,
// => implements the internal IPublishedContentExtended.
// here we assume that either the factory just created a model that implements
// IPublishedContentExtended and therefore does not need to be extended again,
// because it can carry the extra property - or that it did *not* create a
// model and therefore returned the original content unchanged.
var model = content.CreateModel();
var extended = model == content // == means the factory did not create a model
? new PublishedContentExtended(content) // so we have to extend
: model; // else we can use what the factory returned
// so extended should always implement IPublishedContentExtended, however if
// by mistake the factory returned a different object that does not implement
// IPublishedContentExtended (which would be an error), throw.
//
// see also PublishedContentExtensionsForModels.CreateModel
// NOTE
// could we lift that constraint and accept that models just be IPublishedContent?
// would then mean that we cannot assume a model is IPublishedContentExtended, so
// either it is, or we need to wrap it. so instead of having
// (Model:IPublishedContentExtended (IPublishedContent))
// we'd have
// (PublishedContentExtended (Model (IPublishedContent)))
// and it is that bad? any other consequences?
//
// would also allow the factory to cache the model (though that should really
// be done by the content cache, not by the factory).
var extended2 = extended as IPublishedContentExtended;
if (extended2 != null) // always true, but keeps Resharper happy
extended2.SetContentSet(contentSet);
if (extended2 == null)
throw new Exception("Extended does not implement IPublishedContentExtended.");
extended2.SetContentSet(contentSet);
return extended2;
}
@@ -1,4 +1,6 @@
namespace Umbraco.Core.Models.PublishedContent
using System;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Provides strongly typed published content models services.
@@ -12,9 +14,32 @@
/// <returns>The strongly typed published content model.</returns>
public static IPublishedContent CreateModel(this IPublishedContent content)
{
return PublishedContentModelFactoryResolver.Current.HasValue
? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content)
: content;
if (content == null)
return null;
if (PublishedContentModelFactoryResolver.Current.HasValue == false)
return content;
// get model
// if factory returns nothing, throw
// if factory just returns what it got, return
var model = PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content);
if (model == null)
throw new Exception("IPublishedContentFactory returned null.");
if (ReferenceEquals(model, content))
return content;
// at the moment, other parts of our code assume that all models will
// somehow implement IPublishedContentExtended and not just be IPublishedContent,
// so we'd better check this here to fail as soon as we can.
//
// see also PublishedContentExtended.Extend
var extended = model as IPublishedContentExtended;
if (extended == null)
throw new Exception("IPublishedContentFactory created an object that does not implement IPublishedContentModelExtended.");
return model;
}
}
}
@@ -31,7 +31,7 @@ namespace Umbraco.Core.Models.PublishedContent
public PublishedContentModelFactory(IEnumerable<Type> types)
{
var ctorArgTypes = new[] { typeof(IPublishedContent) };
var constructors = new Dictionary<string, Func<IPublishedContent, IPublishedContent>>();
var constructors = new Dictionary<string, Func<IPublishedContent, IPublishedContent>>(StringComparer.InvariantCultureIgnoreCase);
foreach (var type in types)
{
@@ -40,7 +40,6 @@ namespace Umbraco.Core.Models.PublishedContent
throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName));
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
typeName = typeName.ToLowerInvariant();
if (constructors.ContainsKey(typeName))
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
@@ -70,7 +69,7 @@ namespace Umbraco.Core.Models.PublishedContent
return content;
// be case-insensitive
var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant();
var contentTypeAlias = content.DocumentTypeAlias;
//ConstructorInfo constructor;
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
@@ -78,5 +78,23 @@ namespace Umbraco.Core.Models.Rdbms
return string.Empty;
}
}
protected bool Equals(PropertyDataDto other)
{
return Id == other.Id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((PropertyDataDto) obj);
}
public override int GetHashCode()
{
return Id;
}
}
}
+3
View File
@@ -27,5 +27,8 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.Null)]
[Length(100)]
public string Group { get; set; }//NOTE Is set to [varchar] (100) in Sql Server script
[ResultColumn("NodeCount")]
public int NodeCount { get; set; }
}
}
+9 -28
View File
@@ -1,38 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
public class TaggedEntity
{
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
{
EntityId = entityId;
TaggedProperties = taggedProperties;
}
public int EntityId { get; private set; }
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
}
public class TaggedProperty
{
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<Tag> tags)
{
PropertyTypeId = propertyTypeId;
PropertyTypeAlias = propertyTypeAlias;
Tags = tags;
}
public int PropertyTypeId { get; private set; }
public string PropertyTypeAlias { get; private set; }
public IEnumerable<Tag> Tags { get; private set; }
}
[Serializable]
[DataContract(IsReference = true)]
public class Tag : Entity, ITag
@@ -43,9 +16,15 @@ namespace Umbraco.Core.Models
public Tag(int id, string text, string @group)
{
Id = id;
Text = text;
Group = @group;
Id = id;
}
public Tag(int id, string text, string @group, int nodeCount)
: this(id, text, @group)
{
NodeCount = nodeCount;
}
private static readonly PropertyInfo TextSelector = ExpressionHelper.GetPropertyInfo<Tag, string>(x => x.Text);
@@ -79,6 +58,8 @@ namespace Umbraco.Core.Models
}
}
public int NodeCount { get; internal set; }
//TODO: enable this at some stage
//public int ParentId { get; set; }
}
+28
View File
@@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a tagged entity.
/// </summary>
/// <remarks>Note that it is the properties of an entity (like Content, Media, Members, etc.) that is tagged,
/// which is why this class is composed of a list of tagged properties and an Id reference to the actual entity.</remarks>
public class TaggedEntity
{
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
{
EntityId = entityId;
TaggedProperties = taggedProperties;
}
/// <summary>
/// Id of the entity, which is tagged
/// </summary>
public int EntityId { get; private set; }
/// <summary>
/// An enumerable list of tagged properties
/// </summary>
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
}
}
+32
View File
@@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a tagged property on an entity.
/// </summary>
public class TaggedProperty
{
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<ITag> tags)
{
PropertyTypeId = propertyTypeId;
PropertyTypeAlias = propertyTypeAlias;
Tags = tags;
}
/// <summary>
/// Id of the PropertyType, which this tagged property is based on
/// </summary>
public int PropertyTypeId { get; private set; }
/// <summary>
/// Alias of the PropertyType, which this tagged property is based on
/// </summary>
public string PropertyTypeAlias { get; private set; }
/// <summary>
/// An enumerable list of Tags for the property
/// </summary>
public IEnumerable<ITag> Tags { get; private set; }
}
}
@@ -87,13 +87,13 @@ namespace Umbraco.Core.Models
{
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
var attributes = memInfo[0].GetCustomAttributes(typeof(FriendlyNameAttribute),
false);
if (attributes.Length == 0)
return string.Empty;
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
var attribute = ((FriendlyNameAttribute)attributes[0]);
if (attribute == null)
return string.Empty;
+32
View File
@@ -0,0 +1,32 @@
using System;
namespace Umbraco.Core
{
/// <summary>
/// Provides an equivalent to the c# lock statement, to be used in a using block.
/// </summary>
/// <remarks>Ie replace <c>lock (o) {...}</c> by <c>using (new MonitorLock(o)) { ... }</c></remarks>
public class MonitorLock : IDisposable
{
private readonly object _locker;
private readonly bool _entered;
/// <summary>
/// Initializes a new instance of the <see cref="MonitorLock"/> class with an object to lock.
/// </summary>
/// <param name="locker">The object to lock.</param>
/// <remarks>Should always be used within a using block.</remarks>
public MonitorLock(object locker)
{
_locker = locker;
_entered = false;
System.Threading.Monitor.Enter(_locker, ref _entered);
}
void IDisposable.Dispose()
{
if (_entered)
System.Threading.Monitor.Exit(_locker);
}
}
}
@@ -63,6 +63,11 @@ namespace Umbraco.Core.ObjectResolution
}
}
// NOTE - the ugly code below exists only because of umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers
// which wants to re-register actions and handlers instead of properly restarting the application. Don't even think
// about using it for anything else. Also, while the backdoor is open, the resolution system is locked so nothing
// can work properly => deadlocks. Therefore, open the backdoor, do resolution changes EXCLUSIVELY, and close the door!
/// <summary>
/// Returns a disposable object that reprents dirty access to temporarily unfrozen resolution configuration.
/// </summary>
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace Umbraco.Core.Packaging
{
internal class ConflictingPackageData : IConflictingPackageData
{
private readonly IMacroService _macroService;
private readonly IFileService _fileService;
public ConflictingPackageData(IMacroService macroService,
IFileService fileService)
{
if (fileService != null) _fileService = fileService;
else throw new ArgumentNullException("fileService");
if (macroService != null) _macroService = macroService;
else throw new ArgumentNullException("macroService");
}
public IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes)
{
if (string.Equals(Constants.Packaging.StylesheetsNodeName, stylesheetNotes.Name.LocalName) == false)
{
throw new ArgumentException("the root element must be \"" + Constants.Packaging.StylesheetsNodeName + "\"", "stylesheetNotes");
}
return stylesheetNotes.Elements(Constants.Packaging.StylesheetNodeName)
.Select(n =>
{
XElement xElement = n.Element(Constants.Packaging.NameNodeName);
if (xElement == null)
{
throw new ArgumentException("Missing \"" + Constants.Packaging.NameNodeName + "\" element",
"stylesheetNotes");
}
return _fileService.GetStylesheetByName(xElement.Value) as IFile;
})
.Where(v => v != null);
}
public IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes)
{
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateNotes.Name.LocalName) == false)
{
throw new ArgumentException("Node must be a \"" + Constants.Packaging.TemplatesNodeName + "\" node",
"templateNotes");
}
return templateNotes.Elements(Constants.Packaging.TemplateNodeName)
.Select(n =>
{
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall);
if (xElement == null)
{
throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeNameCapital + "\" element",
"templateNotes");
}
return _fileService.GetTemplate(xElement.Value);
})
.Where(v => v != null);
}
public IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes)
{
if (string.Equals(Constants.Packaging.MacrosNodeName, macroNodes.Name.LocalName) == false)
{
throw new ArgumentException("Node must be a \"" + Constants.Packaging.MacrosNodeName + "\" node",
"macroNodes");
}
return macroNodes.Elements(Constants.Packaging.MacroNodeName)
.Select(n =>
{
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital);
if (xElement == null)
{
throw new ArgumentException(string.Format("missing a \"{0}\" element in {0} element", Constants.Packaging.AliasNodeNameSmall),
"macroNodes");
}
return _macroService.GetByAlias(xElement.Value);
})
.Where(v => v != null);
}
}
}
@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Xml.Linq;
using Umbraco.Core.Models;
namespace Umbraco.Core.Packaging
{
internal interface IConflictingPackageData
{
IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes);
IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes);
IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes);
}
}
@@ -0,0 +1,62 @@
using System.Collections.Generic;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Used to access an umbraco package file
/// Remeber that filenames must be unique
/// use "FindDubletFileNames" for sanitycheck
/// </summary>
internal interface IPackageExtraction
{
/// <summary>
/// Returns the content of the file with the given filename
/// </summary>
/// <param name="packageFilePath">Full path to the umbraco package file</param>
/// <param name="fileToRead">filename of the file for wich to get the text content</param>
/// <param name="directoryInPackage">this is the relative directory for the location of the file in the package
/// I dont know why umbraco packages contains directories in the first place??</param>
/// <returns>text content of the file</returns>
string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage);
/// <summary>
/// Copies a file from package to given destination
/// </summary>
/// <param name="packageFilePath">Full path to the ubraco package file</param>
/// <param name="fileInPackageName">filename of the file to copy</param>
/// <param name="destinationfilePath">destination path (including destination filename)</param>
/// <returns>True a file was overwritten</returns>
void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath);
/// <summary>
/// Copies a file from package to given destination
/// </summary>
/// <param name="packageFilePath">Full path to the ubraco package file</param>
/// <param name="sourceDestination">Key: Source file in package. Value: Destination path inclusive file name</param>
void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination);
/// <summary>
/// Check if given list of files can be found in the package
/// </summary>
/// <param name="packageFilePath">Full path to the umbraco package file</param>
/// <param name="expectedFiles">a list of files you would like to find in the package</param>
/// <returns>a subset if any of the files in "expectedFiles" that could not be found in the package</returns>
IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles);
/// <summary>
/// Sanitycheck - should return en empty collection if package is valid
/// </summary>
/// <param name="packageFilePath">Full path to the umbraco package file</param>
/// <returns>list of files that are found more than ones (accross directories) in the package</returns>
IEnumerable<string> FindDubletFileNames(string packageFilePath);
/// <summary>
/// Reads the given files from archive and returns them as a collection of byte arrays
/// </summary>
/// <param name="packageFilePath"></param>
/// <param name="filesToGet"></param>
/// <returns></returns>
IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet);
}
}
@@ -0,0 +1,13 @@
using System.Xml.Linq;
using Umbraco.Core.Packaging.Models;
namespace Umbraco.Core.Packaging
{
internal interface IPackageInstallation
{
InstallationSummary InstallPackage(string packageFilePath, int userId);
MetaData GetMetaData(string packageFilePath);
PreInstallWarnings GetPreInstallWarnings(string packageFilePath);
XElement GetConfigXmlElement(string packageFilePath);
}
}
@@ -1,21 +0,0 @@
using System;
using System.Runtime.Serialization;
using System.Xml;
namespace Umbraco.Core.Packaging.Models
{
[Serializable]
[DataContract(IsReference = true)]
internal class InstallAction
{
public string Alias { get; set; }
public string PackageName { get; set; }
public string RunAt { get; set; }//NOTE Should this default to "install"
public bool Undo { get; set; }
public XmlNode XmlData { get; set; }
}
}
@@ -1,24 +1,44 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Umbraco.Core.Packaging.Models
{
[Serializable]
[DataContract(IsReference = true)]
internal class InstallationSummary
{
public MetaData MetaData { get; set; }
public IEnumerable<int> DataTypesInstalled { get; set; }
public IEnumerable<int> LanguagesInstalled { get; set; }
public IEnumerable<int> DictionaryItemsInstalled { get; set; }
public IEnumerable<int> MacrosInstalled { get; set; }
public IEnumerable<KeyValuePair<string, bool>> FilesInstalled { get; set;}
public IEnumerable<int> TemplatesInstalled { get; set; }
public IEnumerable<int> DocumentTypesInstalled { get; set; }
public IEnumerable<int> StylesheetsInstalled { get; set; }
public IEnumerable<int> DocumentsInstalled { get; set; }
public IEnumerable<InstallAction> InstallActions { get; set; }
public IEnumerable<UninstallAction> UninstallActions { get; set; }
}
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 InstallationSummary
{
public MetaData MetaData { get; set; }
public IEnumerable<IDataTypeDefinition> DataTypesInstalled { get; set; }
public IEnumerable<ILanguage> LanguagesInstalled { get; set; }
public IEnumerable<IDictionaryItem> DictionaryItemsInstalled { get; set; }
public IEnumerable<IMacro> MacrosInstalled { get; set; }
public IEnumerable<string> FilesInstalled { get; set; }
public IEnumerable<ITemplate> TemplatesInstalled { get; set; }
public IEnumerable<IContentType> ContentTypesInstalled { get; set; }
public IEnumerable<IFile> StylesheetsInstalled { get; set; }
public IEnumerable<IContent> ContentInstalled { get; set; }
public IEnumerable<PackageAction> Actions { get; set; }
public bool PackageInstalled { get; set; }
}
internal static class InstallationSummaryExtentions
{
public static InstallationSummary InitEmpty(this InstallationSummary summary)
{
summary.Actions = new List<PackageAction>();
summary.ContentInstalled = new List<IContent>();
summary.ContentTypesInstalled = new List<IContentType>();
summary.DataTypesInstalled = new List<IDataTypeDefinition>();
summary.DictionaryItemsInstalled = new List<IDictionaryItem>();
summary.FilesInstalled = new List<string>();
summary.LanguagesInstalled = new List<ILanguage>();
summary.MacrosInstalled = new List<IMacro>();
summary.MetaData = new MetaData();
summary.TemplatesInstalled = new List<ITemplate>();
summary.PackageInstalled = false;
return summary;
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.Runtime.Serialization;
using System.Xml.Linq;
namespace Umbraco.Core.Packaging.Models
{
internal enum ActionRunAt
{
Undefined = 0,
Install,
Uninstall
}
[Serializable]
[DataContract(IsReference = true)]
internal class PackageAction
{
private ActionRunAt _runAt;
private bool? _undo;
public string Alias { get; set; }
public string PackageName { get; set; }
public ActionRunAt RunAt
{
get { return _runAt == ActionRunAt.Undefined ? ActionRunAt.Install : _runAt; }
set { _runAt = value; }
}
public bool Undo //NOTE: Should thid default to "False"? but the documentation says default "True" (http://our.umbraco.org/wiki/reference/packaging/package-actions)
{
get { return _undo ?? true; }
set { _undo = value; }
}
public XElement XmlData { get; set; }
}
}
@@ -0,0 +1,19 @@
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 PreInstallWarnings
{
public KeyValuePair<string, string>[] UnsecureFiles { get; set; }
public KeyValuePair<string, string>[] FilesReplaced { get; set; }
public IEnumerable<IMacro> ConflictingMacroAliases { get; set; }
public IEnumerable<ITemplate> ConflictingTemplateAliases { get; set; }
public IEnumerable<IFile> ConflictingStylesheetNames { get; set; }
public IEnumerable<string> AssembliesWithLegacyPropertyEditors { get; set; }
}
}
@@ -1,21 +0,0 @@
using System;
using System.Runtime.Serialization;
using System.Xml;
namespace Umbraco.Core.Packaging.Models
{
[Serializable]
[DataContract(IsReference = true)]
internal class UninstallAction
{
public string Alias { get; set; }
public string PackageName { get; set; }
public string RunAt { get; set; }//NOTE Should this default to "install"
public bool Undo { get; set; }//NOTE: Should thid default to "False"?
public XmlNode XmlData { get; set; }
}
}
@@ -5,15 +5,47 @@ using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Packaging
{
// Note
// That class uses ReflectionOnlyLoad which does NOT handle policies (bindingRedirect) and
// therefore raised warnings when installing a package, if an exact dependency could not be
// found, though it would be found via policies. So we have to explicitely apply policies
// where appropriate.
internal class PackageBinaryInspector : MarshalByRefObject
{
/// <summary>
/// Entry point to call from your code
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblys"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
/// <remarks>
/// Will perform the assembly scan in a separate app domain
/// </remarks>
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(IEnumerable<byte[]> assemblys, out string[] errorReport)
{
var appDomain = GetTempAppDomain();
var type = typeof(PackageBinaryInspector);
try
{
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
// do NOT turn PerformScan into static (even if ReSharper says so)!
var result = value.PerformScan<T>(assemblys.ToArray(), out errorReport);
return result;
}
finally
{
AppDomain.Unload(appDomain);
}
}
/// <summary>
/// Entry point to call from your code
/// </summary>
@@ -33,6 +65,7 @@ namespace Umbraco.Core.Packaging
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
// do NOT turn PerformScan into static (even if ReSharper says so)!
var result = value.PerformScan<T>(dllPath, out errorReport);
return result;
}
@@ -42,6 +75,35 @@ namespace Umbraco.Core.Packaging
}
}
/// <summary>
/// Performs the assembly scanning
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
/// <remarks>
/// This method is executed in a separate app domain
/// </remarks>
private IEnumerable<string> PerformScan<T>(IEnumerable<byte[]> assemblies, out string[] errorReport)
{
//we need this handler to resolve assembly dependencies when loading below
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
{
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
var a = Assembly.ReflectionOnlyLoad(name);
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
return a;
};
//First load each dll file into the context
// do NOT apply policy here: we want to scan the dlls that are in the binaries
var loaded = assemblies.Select(Assembly.ReflectionOnlyLoad).ToList();
//scan
return PerformScan<T>(loaded, out errorReport);
}
/// <summary>
/// Performs the assembly scanning
/// </summary>
@@ -52,33 +114,42 @@ namespace Umbraco.Core.Packaging
/// <remarks>
/// This method is executed in a separate app domain
/// </remarks>
internal IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
private IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
{
if (Directory.Exists(dllPath) == false)
{
throw new DirectoryNotFoundException("Could not find directory " + dllPath);
}
//we need this handler to resolve assembly dependencies when loading below
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
{
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
var a = Assembly.ReflectionOnlyLoad(name);
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
return a;
};
//First load each dll file into the context
// do NOT apply policy here: we want to scan the dlls that are in the path
var files = Directory.GetFiles(dllPath, "*.dll");
var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList();
//scan
return PerformScan<T>(loaded, out errorReport);
}
private static IEnumerable<string> PerformScan<T>(IList<Assembly> loaded, out string[] errorReport)
{
var dllsWithReference = new List<string>();
var errors = new List<string>();
var assembliesWithErrors = new List<Assembly>();
//we need this handler to resolve assembly dependencies below
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
{
var a = Assembly.ReflectionOnlyLoad(e.Name);
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
return a;
};
//First load each dll file into the context
var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList();
//load each of the LoadFrom assemblies into the Load context too
foreach (var a in loaded)
{
Assembly.ReflectionOnlyLoad(a.FullName);
var name = AppDomain.CurrentDomain.ApplyPolicy(a.FullName);
Assembly.ReflectionOnlyLoad(name);
}
//get the list of assembly names to compare below
@@ -94,7 +165,8 @@ namespace Umbraco.Core.Packaging
{
try
{
Assembly.ReflectionOnlyLoad(assemblyName.FullName);
var name = AppDomain.CurrentDomain.ApplyPolicy(assemblyName.FullName);
Assembly.ReflectionOnlyLoad(name);
}
catch (FileNotFoundException)
{
@@ -167,7 +239,6 @@ namespace Umbraco.Core.Packaging
return dllsWithReference;
}
/// <summary>
/// In order to compare types, the types must be in the same context, this method will return the type that
/// we are checking against but from the Load context.
@@ -176,7 +247,8 @@ namespace Umbraco.Core.Packaging
/// <returns></returns>
private static Type GetLoadFromContractType<T>()
{
var contractAssemblyLoadFrom =Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName);
var name = AppDomain.CurrentDomain.ApplyPolicy(typeof(T).Assembly.FullName);
var contractAssemblyLoadFrom = Assembly.ReflectionOnlyLoad(name);
var contractType = contractAssemblyLoadFrom.GetExportedTypes()
.FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName);
@@ -216,13 +288,5 @@ namespace Umbraco.Core.Packaging
domainSetup,
new PermissionSet(PermissionState.Unrestricted));
}
private static string GetAssemblyPath(Assembly a)
{
var codeBase = a.CodeBase;
var uri = new Uri(codeBase);
return uri.LocalPath;
}
}
}
@@ -8,9 +8,9 @@ namespace Umbraco.Core.Packaging
internal class PackageBuilding : IPackageBuilding
{
private readonly PackagingService _packagingService;
private readonly IPackagingService _packagingService;
public PackageBuilding(PackagingService packagingService)
public PackageBuilding(IPackagingService packagingService)
{
_packagingService = packagingService;
}
+200 -33
View File
@@ -1,34 +1,201 @@
using System;
using System.IO;
using Umbraco.Core.IO;
namespace Umbraco.Core.Packaging
{
internal interface IPackageExtraction
{
bool Extract(string packageFilePath, string destinationFolder);
string ExtractToTemporaryFolder(string packageFilePath);
string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml");
}
internal class PackageExtraction : IPackageExtraction
{
public bool Extract(string packageFilePath, string destinationFolder)
{
return true;
}
public string ExtractToTemporaryFolder(string packageFilePath)
{
string tempDir = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), Guid.NewGuid().ToString("D"));
Directory.CreateDirectory(tempDir);
Extract(packageFilePath, tempDir);
return tempDir;
}
public string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml")
{
return string.Empty;
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.SharpZipLib.Zip;
namespace Umbraco.Core.Packaging
{
internal class PackageExtraction : IPackageExtraction
{
public string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage)
{
string retVal = null;
bool fileFound = false;
string foundDir = null;
ReadZipfileEntries(packageFilePath, (entry, stream) =>
{
string fileName = Path.GetFileName(entry.Name);
if (string.IsNullOrEmpty(fileName) == false &&
fileName.Equals(fileToRead, StringComparison.CurrentCultureIgnoreCase))
{
foundDir = entry.Name.Substring(0, entry.Name.Length - fileName.Length);
fileFound = true;
using (var reader = new StreamReader(stream))
{
retVal = reader.ReadToEnd();
return false;
}
}
return true;
});
if (fileFound == false)
{
directoryInPackage = null;
throw new FileNotFoundException(string.Format("Could not find file in package {0}", packageFilePath), fileToRead);
}
directoryInPackage = foundDir;
return retVal;
}
private static void CheckPackageExists(string packageFilePath)
{
if (string.IsNullOrEmpty(packageFilePath))
{
throw new ArgumentNullException("packageFilePath");
}
if (File.Exists(packageFilePath) == false)
{
if (File.Exists(packageFilePath) == false)
throw new ArgumentException(string.Format("Package file: {0} could not be found", packageFilePath));
}
string extension = Path.GetExtension(packageFilePath).ToLower();
var alowedExtension = new[] { ".umb", ".zip" };
// Check if the file is a valid package
if (alowedExtension.All(ae => ae.Equals(extension) == false))
{
throw new ArgumentException(
string.Format("Error - file isn't a package. only extentions: \"{0}\" is allowed", string.Join(", ", alowedExtension)));
}
}
public void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath)
{
CopyFilesFromArchive(packageFilePath, new[]{new KeyValuePair<string, string>(fileInPackageName, destinationfilePath) } );
}
public void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination)
{
var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value);
ReadZipfileEntries(packageFilePath, (entry, stream) =>
{
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
if (fileName == string.Empty) { return true; }
string destination;
if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination))
{
using (var streamWriter = File.Open(destination, FileMode.Create))
{
stream.CopyTo(streamWriter);
}
d.Remove(fileName);
return d.Any();
}
return true;
});
if (d.Any())
{
throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFilePath));
}
}
public IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles)
{
var retVal = expectedFiles.ToList();
ReadZipfileEntries(packageFilePath, (zipEntry, stream) =>
{
string fileName = Path.GetFileName(zipEntry.Name);
int index = retVal.FindIndex(f => f.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));
if (index != -1) { retVal.RemoveAt(index); }
return retVal.Any();
});
return retVal;
}
public IEnumerable<string> FindDubletFileNames(string packageFilePath)
{
var dictionary = new Dictionary<string, List<string>>();
ReadZipfileEntries(packageFilePath, (entry, stream) =>
{
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
List<string> list;
if (dictionary.TryGetValue(fileName, out list) == false)
{
list = new List<string>();
dictionary.Add(fileName, list);
}
list.Add(entry.Name);
return true;
});
return dictionary.Values.Where(v => v.Count > 1).SelectMany(v => v);
}
public IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet)
{
CheckPackageExists(packageFilePath);
var files = new HashSet<string>(filesToGet.Select(f => f.ToLower()));
using (var fs = File.OpenRead(packageFilePath))
{
using (var zipInputStream = new ZipInputStream(fs))
{
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
{
if (zipEntry.IsDirectory) continue;
if (files.Contains(zipEntry.Name))
{
using (var memStream = new MemoryStream())
{
zipInputStream.CopyTo(memStream);
yield return memStream.ToArray();
memStream.Close();
}
}
}
zipInputStream.Close();
}
fs.Close();
}
}
private void ReadZipfileEntries(string packageFilePath, Func<ZipEntry, ZipInputStream, bool> entryFunc, bool skipsDirectories = true)
{
CheckPackageExists(packageFilePath);
using (var fs = File.OpenRead(packageFilePath))
{
using (var zipInputStream = new ZipInputStream(fs))
{
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
{
if (zipEntry.IsDirectory && skipsDirectories) continue;
if (entryFunc(zipEntry, zipInputStream) == false) break;
}
zipInputStream.Close();
}
fs.Close();
}
}
}
}
+604 -34
View File
@@ -1,35 +1,605 @@
using Umbraco.Core.Packaging.Models;
using Umbraco.Core.Services;
namespace Umbraco.Core.Packaging
{
internal interface IPackageInstallation
{
InstallationSummary InstallPackage(string packageFilePath, int userId = 0);
MetaData GetPackageMetaData(string packageFilePath);
}
internal class PackageInstallation : IPackageInstallation
{
private readonly PackagingService _packagingService;
private readonly PackageExtraction _packageExtraction;
public PackageInstallation(PackagingService packagingService, PackageExtraction packageExtraction)
{
_packagingService = packagingService;
_packageExtraction = packageExtraction;
}
public InstallationSummary InstallPackage(string packageFilePath, int userId = 0)
{
var summary = new InstallationSummary();
return summary;
}
public MetaData GetPackageMetaData(string packageFilePath)
{
var metaData = new MetaData();
return metaData;
}
}
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Packaging.Models;
using Umbraco.Core.Services;
using umbraco.interfaces;
using File = System.IO.File;
namespace Umbraco.Core.Packaging
{
internal class PackageInstallation : IPackageInstallation
{
private readonly IFileService _fileService;
private readonly IMacroService _macroService;
private readonly IPackagingService _packagingService;
private IConflictingPackageData _conflictingPackageData;
private readonly IPackageExtraction _packageExtraction;
private string _fullPathToRoot;
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
IFileService fileService, IPackageExtraction packageExtraction)
: this(packagingService, macroService, fileService, packageExtraction, GlobalSettings.FullpathToRoot)
{}
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
IFileService fileService, IPackageExtraction packageExtraction, string fullPathToRoot)
{
if (packageExtraction != null) _packageExtraction = packageExtraction;
else throw new ArgumentNullException("packageExtraction");
if (macroService != null) _macroService = macroService;
else throw new ArgumentNullException("macroService");
if (fileService != null) _fileService = fileService;
else throw new ArgumentNullException("fileService");
if (packagingService != null) _packagingService = packagingService;
else throw new ArgumentNullException("packagingService");
_fullPathToRoot = fullPathToRoot;
}
public IConflictingPackageData ConflictingPackageData
{
private get
{
return _conflictingPackageData ??
(_conflictingPackageData = new ConflictingPackageData(_macroService, _fileService));
}
set
{
if (_conflictingPackageData != null)
{
throw new PropertyConstraintException("This property already have a value");
}
_conflictingPackageData = value;
}
}
public string FullPathToRoot
{
private get { return _fullPathToRoot; }
set
{
if (_fullPathToRoot != null)
{
throw new PropertyConstraintException("This property already have a value");
}
_fullPathToRoot = value;
}
}
public MetaData GetMetaData(string packageFilePath)
{
try
{
XElement rootElement = GetConfigXmlElement(packageFilePath);
return GetMetaData(rootElement);
}
catch (Exception e)
{
throw new Exception("Error reading " + packageFilePath, e);
}
}
public PreInstallWarnings GetPreInstallWarnings(string packageFilePath)
{
try
{
XElement rootElement = GetConfigXmlElement(packageFilePath);
return GetPreInstallWarnings(packageFilePath, rootElement);
}
catch (Exception e)
{
throw new Exception("Error reading " + packageFilePath, e);
}
}
public InstallationSummary InstallPackage(string packageFile, int userId)
{
XElement dataTypes;
XElement languages;
XElement dictionaryItems;
XElement macroes;
XElement files;
XElement templates;
XElement documentTypes;
XElement styleSheets;
XElement documentSet;
XElement documents;
XElement actions;
MetaData metaData;
InstallationSummary installationSummary;
try
{
XElement rootElement = GetConfigXmlElement(packageFile);
PackageSupportedCheck(rootElement);
PackageStructureSanetyCheck(packageFile, rootElement);
dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName);
languages = rootElement.Element(Constants.Packaging.LanguagesNodeName);
dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName);
macroes = rootElement.Element(Constants.Packaging.MacrosNodeName);
files = rootElement.Element(Constants.Packaging.FilesNodeName);
templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName);
styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName);
documents = rootElement.Element(Constants.Packaging.DocumentsNodeName);
actions = rootElement.Element(Constants.Packaging.ActionsNodeName);
metaData = GetMetaData(rootElement);
installationSummary = new InstallationSummary {MetaData = metaData};
}
catch (Exception e)
{
throw new Exception("Error reading " + packageFile, e);
}
try
{
var dataTypeDefinitions = EmptyEnumerableIfNull<IDataTypeDefinition>(dataTypes) ?? InstallDataTypes(dataTypes, userId);
installationSummary.DataTypesInstalled = dataTypeDefinitions;
var languagesInstalled = EmptyEnumerableIfNull<ILanguage>(languages) ?? InstallLanguages(languages, userId);
installationSummary.LanguagesInstalled = languagesInstalled;
var dictionaryInstalled = EmptyEnumerableIfNull<IDictionaryItem>(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems);
installationSummary.DictionaryItemsInstalled = dictionaryInstalled;
var macros = EmptyEnumerableIfNull<IMacro>(macroes) ?? InstallMacros(macroes, userId);
installationSummary.MacrosInstalled = macros;
var keyValuePairs = EmptyEnumerableIfNull<string>(packageFile) ?? InstallFiles(packageFile, files);
installationSummary.FilesInstalled = keyValuePairs;
var templatesInstalled = EmptyEnumerableIfNull<ITemplate>(templates) ?? InstallTemplats(templates, userId);
installationSummary.TemplatesInstalled = templatesInstalled;
var documentTypesInstalled = EmptyEnumerableIfNull<IContentType>(documentTypes) ?? InstallDocumentTypes(documentTypes, userId);
installationSummary.ContentTypesInstalled =documentTypesInstalled;
var stylesheetsInstalled = EmptyEnumerableIfNull<IFile>(styleSheets) ?? InstallStylesheets(styleSheets, userId);
installationSummary.StylesheetsInstalled = stylesheetsInstalled;
var documentsInstalled = documents != null ? InstallDocuments(documents, userId)
: EmptyEnumerableIfNull<IContent>(documentSet)
?? InstallDocuments(documentSet, userId);
installationSummary.ContentInstalled = documentsInstalled;
var packageActions = EmptyEnumerableIfNull<PackageAction>(actions) ?? GetPackageActions(actions, metaData.Name);
installationSummary.Actions = packageActions;
installationSummary.PackageInstalled = true;
return installationSummary;
}
catch (Exception e)
{
throw new Exception("Error installing package " + packageFile, e);
}
}
/// <summary>
/// Temperary check to test that we support stylesheets
/// </summary>
/// <param name="rootElement"></param>
private void PackageSupportedCheck(XElement rootElement)
{
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
if (styleSheets != null && styleSheets.Elements().Any())
throw new NotSupportedException("Stylesheets is not suported in this version of umbraco");
}
private static T[] EmptyArrayIfNull<T>(object obj)
{
return obj == null ? new T[0] : null;
}
private static IEnumerable<T> EmptyEnumerableIfNull<T>(object obj)
{
return obj == null ? Enumerable.Empty<T>() : null;
}
private XDocument GetConfigXmlDoc(string packageFilePath)
{
string filePathInPackage;
string configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFilePath,
Constants.Packaging.PackageXmlFileName, out filePathInPackage);
return XDocument.Parse(configXmlContent);
}
public XElement GetConfigXmlElement(string packageFilePath)
{
XDocument document = GetConfigXmlDoc(packageFilePath);
if (document.Root == null ||
document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false)
{
throw new ArgumentException("xml does not have a root node called \"umbPackage\"", packageFilePath);
}
return document.Root;
}
internal void PackageStructureSanetyCheck(string packageFilePath)
{
XElement rootElement = GetConfigXmlElement(packageFilePath);
PackageStructureSanetyCheck(packageFilePath, rootElement);
}
private void PackageStructureSanetyCheck(string packageFilePath, XElement rootElement)
{
XElement filesElement = rootElement.Element(Constants.Packaging.FilesNodeName);
if (filesElement != null)
{
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement).ToArray();
var missingFiles = _packageExtraction.FindMissingFiles(packageFilePath, sourceDestination.Select(i => i.Key)).ToArray();
if (missingFiles.Any())
{
throw new Exception("The following file(s) are missing in the package: " +
string.Join(", ", missingFiles.Select(
mf =>
{
var sd = sourceDestination.Single(fi => fi.Key == mf);
return string.Format("source: \"{0}\" destination: \"{1}\"",
sd.Key, sd.Value);
})));
}
IEnumerable<string> dubletFileNames = _packageExtraction.FindDubletFileNames(packageFilePath).ToArray();
if (dubletFileNames.Any())
{
throw new Exception("The following filename(s) are found more than one time in the package, since the filename is used ad primary key, this is not allowed: " +
string.Join(", ", dubletFileNames));
}
}
}
private static IEnumerable<PackageAction> GetPackageActions(XElement actionsElement, string packageName)
{
if (actionsElement == null) { return new PackageAction[0]; }
if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root",
"actionsElement");
}
return actionsElement.Elements(Constants.Packaging.ActionNodeName)
.Select(elemet =>
{
XAttribute aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeNameCapital);
if (aliasAttr == null)
throw new ArgumentException(
"missing \"" + Constants.Packaging.AliasNodeNameCapital + "\" atribute in alias element",
"actionsElement");
var packageAction = new PackageAction
{
XmlData = elemet,
Alias = aliasAttr.Value,
PackageName = packageName,
};
XAttribute attr = elemet.Attribute(Constants.Packaging.RunatNodeAttribute);
ActionRunAt runAt;
if (attr != null && Enum.TryParse(attr.Value, true, out runAt)) { packageAction.RunAt = runAt; }
attr = elemet.Attribute(Constants.Packaging.UndoNodeAttribute);
bool undo;
if (attr != null && bool.TryParse(attr.Value, out undo)) { packageAction.Undo = undo; }
return packageAction;
}).ToArray();
}
private IEnumerable<IContent> InstallDocuments(XElement documentsElement, int userId = 0)
{
if ((string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName) == false)
&& (string.Equals(Constants.Packaging.DocumentsNodeName, documentsElement.Name.LocalName) == false))
{
throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentsNodeName + "\" as root",
"documentsElement");
}
if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName))
return _packagingService.ImportContent(documentsElement, -1, userId);
return
documentsElement.Elements(Constants.Packaging.DocumentSetNodeName)
.SelectMany(documentSetElement => _packagingService.ImportContent(documentSetElement, -1, userId))
.ToArray();
}
private IEnumerable<IFile> InstallStylesheets(XElement styleSheetsElement, int userId = 0)
{
if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root",
"styleSheetsElement");
}
// TODO: Call _packagingService when import stylesheets import has been implimentet
if (styleSheetsElement.HasElements == false) { return new List<IFile>(); }
throw new NotImplementedException("The packaging service do not yes have a method for importing stylesheets");
}
private IEnumerable<IContentType> InstallDocumentTypes(XElement documentTypes, int userId = 0)
{
if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName) == false)
{
if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName) == false)
throw new ArgumentException(
"Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes");
documentTypes = new XElement(Constants.Packaging.DocumentTypesNodeName, documentTypes);
}
return _packagingService.ImportContentTypes(documentTypes, userId);
}
private IEnumerable<ITemplate> InstallTemplats(XElement templateElement, int userId = 0)
{
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root",
"templateElement");
}
return _packagingService.ImportTemplates(templateElement, userId);
}
private IEnumerable<string> InstallFiles(string packageFilePath, XElement filesElement)
{
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement);
sourceDestination = AppendRootToDestination(FullPathToRoot, sourceDestination);
_packageExtraction.CopyFilesFromArchive(packageFilePath, sourceDestination);
return sourceDestination.Select(sd => sd.Value).ToArray();
}
private KeyValuePair<string, string>[] AppendRootToDestination(string fullpathToRoot, IEnumerable<KeyValuePair<string, string>> sourceDestination)
{
return
sourceDestination.Select(
sd => new KeyValuePair<string, string>(sd.Key, Path.Combine(fullpathToRoot, sd.Value))).ToArray();
}
private IEnumerable<IMacro> InstallMacros(XElement macroElements, int userId = 0)
{
if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root",
"macroElements");
}
return _packagingService.ImportMacros(macroElements, userId);
}
private IEnumerable<IDictionaryItem> InstallDictionaryItems(XElement dictionaryItemsElement)
{
if (string.Equals(Constants.Packaging.DictionaryItemsNodeName, dictionaryItemsElement.Name.LocalName) ==
false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryItemsNodeName + "\" as root",
"dictionaryItemsElement");
}
return _packagingService.ImportDictionaryItems(dictionaryItemsElement);
}
private IEnumerable<ILanguage> InstallLanguages(XElement languageElement, int userId = 0)
{
if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.LanguagesNodeName + "\" as root", "languageElement");
}
return _packagingService.ImportLanguages(languageElement, userId);
}
private IEnumerable<IDataTypeDefinition> InstallDataTypes(XElement dataTypeElements, int userId = 0)
{
if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName) == false)
{
if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.DataTypeNodeName + "\" as root", "dataTypeElements");
}
}
return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId);
}
private PreInstallWarnings GetPreInstallWarnings(string packagePath, XElement rootElement)
{
XElement files = rootElement.Element(Constants.Packaging.FilesNodeName);
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
XElement templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
XElement alias = rootElement.Element(Constants.Packaging.MacrosNodeName);
var sourceDestination = EmptyArrayIfNull<KeyValuePair<string, string>>(files) ?? ExtractSourceDestinationFileInformation(files);
var installWarnings = new PreInstallWarnings();
var macroAliases = EmptyEnumerableIfNull<IMacro>(alias) ?? ConflictingPackageData.FindConflictingMacros(alias);
installWarnings.ConflictingMacroAliases = macroAliases;
var templateAliases = EmptyEnumerableIfNull<ITemplate>(templates) ?? ConflictingPackageData.FindConflictingTemplates(templates);
installWarnings.ConflictingTemplateAliases = templateAliases;
var stylesheetNames = EmptyEnumerableIfNull<IFile>(styleSheets) ?? ConflictingPackageData.FindConflictingStylesheets(styleSheets);
installWarnings.ConflictingStylesheetNames = stylesheetNames;
installWarnings.UnsecureFiles = FindUnsecureFiles(sourceDestination);
installWarnings.FilesReplaced = FindFilesToBeReplaced(sourceDestination);
installWarnings.AssembliesWithLegacyPropertyEditors = FindLegacyPropertyEditors(packagePath, sourceDestination);
return installWarnings;
}
private KeyValuePair<string, string>[] FindFilesToBeReplaced(IEnumerable<KeyValuePair<string, string>> sourceDestination)
{
return sourceDestination.Where(sd => File.Exists(Path.Combine(FullPathToRoot, sd.Value))).ToArray();
}
private IEnumerable<string> FindLegacyPropertyEditors(string packagePath, IEnumerable<KeyValuePair<string, string>> sourceDestinationPair)
{
var dlls = sourceDestinationPair.Where(
sd => (Path.GetExtension(sd.Value) ?? string.Empty).Equals(".dll", StringComparison.InvariantCultureIgnoreCase)).Select(sd => sd.Key).ToArray();
if (dlls.Any() == false) { return new List<string>(); }
// Now we want to see if the DLLs contain any legacy data types since we want to warn people about that
string[] assemblyErrors;
IEnumerable<byte[]> assemblyesToScan =_packageExtraction.ReadFilesFromArchive(packagePath, dlls);
return PackageBinaryInspector.ScanAssembliesForTypeReference<IDataType>(assemblyesToScan, out assemblyErrors).ToArray();
}
private KeyValuePair<string, string>[] FindUnsecureFiles(IEnumerable<KeyValuePair<string, string>> sourceDestinationPair)
{
return sourceDestinationPair.Where(sd => IsFileDestinationUnsecure(sd.Value)).ToArray();
}
private bool IsFileDestinationUnsecure(string destination)
{
var unsecureDirNames = new[] {"bin", "app_code"};
if(unsecureDirNames.Any(ud => destination.StartsWith(ud, StringComparison.InvariantCultureIgnoreCase)))
return true;
string extension = Path.GetExtension(destination);
return extension != null && extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase);
}
private KeyValuePair<string, string>[] ExtractSourceDestinationFileInformation(XElement filesElement)
{
if (string.Equals(Constants.Packaging.FilesNodeName, filesElement.Name.LocalName) == false)
{
throw new ArgumentException("the root element must be \"Files\"", "filesElement");
}
return filesElement.Elements(Constants.Packaging.FileNodeName)
.Select(e =>
{
XElement guidElement = e.Element(Constants.Packaging.GuidNodeName);
if (guidElement == null)
{
throw new ArgumentException("Missing element \"" + Constants.Packaging.GuidNodeName + "\"",
"filesElement");
}
XElement orgPathElement = e.Element(Constants.Packaging.OrgPathNodeName);
if (orgPathElement == null)
{
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgPathNodeName + "\"",
"filesElement");
}
XElement orgNameElement = e.Element(Constants.Packaging.OrgNameNodeName);
if (orgNameElement == null)
{
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgNameNodeName + "\"",
"filesElement");
}
var fileName = PrepareAsFilePathElement(orgNameElement.Value);
var relativeDir = UpdatePathPlaceholders(PrepareAsFilePathElement(orgPathElement.Value));
var relativePath = Path.Combine(relativeDir, fileName);
return new KeyValuePair<string, string>(guidElement.Value, relativePath);
}).ToArray();
}
private static string PrepareAsFilePathElement(string pathElement)
{
return pathElement.TrimStart(new[] {'\\', '/', '~'}).Replace("/", "\\");
}
private MetaData GetMetaData(XElement xRootElement)
{
XElement infoElement = xRootElement.Element(Constants.Packaging.InfoNodeName);
if (infoElement == null)
{
throw new ArgumentException("Did not hold a \"" + Constants.Packaging.InfoNodeName + "\" element",
"xRootElement");
}
var majorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMajorXpath);
var minorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMinorXpath);
var patchElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsPatchXpath);
var nameElement = infoElement.XPathSelectElement(Constants.Packaging.PackageNameXpath);
var versionElement = infoElement.XPathSelectElement(Constants.Packaging.PackageVersionXpath);
var urlElement = infoElement.XPathSelectElement(Constants.Packaging.PackageUrlXpath);
var licenseElement = infoElement.XPathSelectElement(Constants.Packaging.PackageLicenseXpath);
var authorNameElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorNameXpath);
var authorUrlElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorWebsiteXpath);
var readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath);
XElement controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName);
return new MetaData
{
Name = StringValue(nameElement),
Version = StringValue(versionElement),
Url = StringValue(urlElement),
License = StringValue(licenseElement),
LicenseUrl = StringAttribute(licenseElement, Constants.Packaging.PackageLicenseXpathUrlAttribute),
AuthorName = StringValue(authorNameElement),
AuthorUrl = StringValue(authorUrlElement),
Readme = StringValue(readmeElement),
Control = StringValue(controlElement),
ReqMajor = IntValue(majorElement),
ReqMinor = IntValue(minorElement),
ReqPatch = IntValue(patchElement)
};
}
private static string StringValue(XElement xElement, string defaultValue = "")
{
return xElement == null ? defaultValue : xElement.Value;
}
private static string StringAttribute(XElement xElement, string attribute, string defaultValue = "")
{
return xElement == null
? defaultValue
: xElement.HasAttributes ? xElement.AttributeValue<string>(attribute) : defaultValue;
}
private static int IntValue(XElement xElement, int defaultValue = 0)
{
int val;
return xElement == null ? defaultValue : int.TryParse(xElement.Value, out val) ? val : defaultValue;
}
private static string UpdatePathPlaceholders(string path)
{
if (path.Contains("[$"))
{
//this is experimental and undocumented...
path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco);
path = path.Replace("[$UMBRACOCLIENT]", SystemDirectories.UmbracoClient);
path = path.Replace("[$CONFIG]", SystemDirectories.Config);
path = path.Replace("[$DATA]", SystemDirectories.Data);
}
return path;
}
}
}
@@ -41,7 +41,8 @@ namespace Umbraco.Core.Persistence.Caching
public static RuntimeCacheProvider Current { get { return lazy.Value; } }
private RuntimeCacheProvider()
//internal for testing! - though I'm not a huge fan of these being singletons!
internal RuntimeCacheProvider()
{
if (HttpContext.Current == null)
{
@@ -246,7 +247,7 @@ namespace Umbraco.Core.Persistence.Caching
private string GetCompositeId(Type type, Guid id)
{
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id.ToString());
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id);
}
private string GetCompositeId(Type type, int id)
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Factories
};
if (entity.HasIdentity)
dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
dto.Id = int.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
return dto;
}
@@ -69,4 +69,4 @@ namespace Umbraco.Core.Persistence.Factories
return list;
}
}
}
}
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Factories
ComputerName = entity.ComputerName
};
if (entity.HasIdentity)
dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
dto.Id = int.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
return dto;
}
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Factories
{
public ITag BuildEntity(TagDto dto)
{
var model = new Tag(dto.Id, dto.Tag, dto.Group);
var model = new Tag(dto.Id, dto.Tag, dto.Group, dto.NodeCount);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
model.ResetDirtyProperties(false);
@@ -20,7 +20,8 @@ namespace Umbraco.Core.Persistence.Factories
{
Id = entity.Id,
Group = entity.Group,
Tag = entity.Text
Tag = entity.Text,
NodeCount = entity.NodeCount,
};
}
}
@@ -132,10 +132,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
private void CreateCmsContentTypeData()
{
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "mediaPhoto.png" });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "mediaFile.png" });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "folder.png" });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "icon-folder", IsContainer = false, AllowAtRoot = true });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "icon-picture" });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "icon-document" });
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "icon-user" });
}
private void CreateUmbracoUserData()
@@ -255,7 +255,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
private void CreateUmbracoRelationTypeData()
{
_database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = "relateDocumentOnCopy", ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), Dual = true, Name = "Relate Document On Copy" });
_database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), Dual = true, Name = Constants.Conventions.RelationTypes.RelateDocumentOnCopyName });
}
private void CreateCmsTaskTypeData()
@@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.Migrations
/// <param name="database">The PetaPoco Database, which the migrations will be run against</param>
/// <param name="isUpgrade">Boolean indicating whether this is an upgrade or downgrade</param>
/// <returns><c>True</c> if migrations were applied, otherwise <c>False</c></returns>
public bool Execute(Database database, bool isUpgrade = true)
public virtual bool Execute(Database database, bool isUpgrade = true)
{
return Execute(database, database.GetDatabaseProvider(), isUpgrade);
}
@@ -42,11 +42,11 @@ namespace Umbraco.Core.Persistence.Migrations
/// <param name="databaseProvider"></param>
/// <param name="isUpgrade">Boolean indicating whether this is an upgrade or downgrade</param>
/// <returns><c>True</c> if migrations were applied, otherwise <c>False</c></returns>
public bool Execute(Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
public virtual bool Execute(Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
{
LogHelper.Info<MigrationRunner>("Initializing database migrations");
var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
var foundMigrations = FindMigrations();
//filter all non-schema migrations
var migrations = isUpgrade
@@ -54,6 +54,7 @@ namespace Umbraco.Core.Persistence.Migrations
: OrderedDowngradeMigrations(foundMigrations).ToList();
//SD: Why do we want this?
//MCH: Because extensibility ... Mostly relevant to package developers who needs to utilize this type of event to add or remove migrations from the list
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
return false;
@@ -69,7 +70,6 @@ namespace Umbraco.Core.Persistence.Migrations
//if this fails then the transaction will be rolled back, BUT if we are using MySql this is not the case,
//since it does not support schema changes in a transaction, see: http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
//so in that case we have to downgrade
if (databaseProvider == DatabaseProviders.MySql)
{
throw new DataLossException(
@@ -86,28 +86,57 @@ namespace Umbraco.Core.Persistence.Migrations
return true;
}
private void ExecuteMigrations(IMigrationContext context, Database database)
/// <summary>
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
/// </summary>
/// <param name="foundMigrations"></param>
/// <returns></returns>
public IEnumerable<IMigration> OrderedUpgradeMigrations(IEnumerable<IMigration> foundMigrations)
{
//Transactional execution of the sql that was generated from the found migrations
using (var transaction = database.GetTransaction())
{
int i = 1;
foreach (var expression in context.Expressions)
{
var sql = expression.Process(database);
if (string.IsNullOrEmpty(sql))
{
i++;
continue;
}
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
select migration).Distinct();
return migrations;
}
LogHelper.Info<MigrationRunner>("Executing sql statement " + i + ": " + sql);
database.Execute(sql);
i++;
}
/// <summary>
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
/// </summary>
/// <param name="foundMigrations"></param>
/// <returns></returns>
public IEnumerable<IMigration> OrderedDowngradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
select migration).Distinct();
return migrations;
}
transaction.Complete();
}
/// <summary>
/// Find all migrations that are available through the <see cref="MigrationResolver"/>
/// </summary>
/// <returns>An array of <see cref="IMigration"/></returns>
protected virtual IMigration[] FindMigrations()
{
//MCH NOTE: Consider adding the ProductName filter to the Resolver so we don't get a bunch of irrelevant migrations
return MigrationResolver.Current.Migrations.ToArray();
}
internal MigrationContext InitializeMigrations(List<IMigration> migrations, Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
@@ -150,48 +179,28 @@ namespace Umbraco.Core.Persistence.Migrations
return context;
}
/// <summary>
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
/// </summary>
/// <param name="foundMigrations"></param>
/// <returns></returns>
internal IEnumerable<IMigration> OrderedUpgradeMigrations(IEnumerable<IMigration> foundMigrations)
private void ExecuteMigrations(IMigrationContext context, Database database)
{
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
select migration).Distinct();
return migrations;
}
//Transactional execution of the sql that was generated from the found migrations
using (var transaction = database.GetTransaction())
{
int i = 1;
foreach (var expression in context.Expressions)
{
var sql = expression.Process(database);
if (string.IsNullOrEmpty(sql))
{
i++;
continue;
}
/// <summary>
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
/// </summary>
/// <param name="foundMigrations"></param>
/// <returns></returns>
public IEnumerable<IMigration> OrderedDowngradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
select migration).Distinct();
return migrations;
LogHelper.Info<MigrationRunner>("Executing sql statement " + i + ": " + sql);
database.Execute(sql);
i++;
}
transaction.Complete();
}
}
/// <summary>
+264 -187
View File
@@ -13,6 +13,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Security;
using System.Security.Permissions;
using System.Text;
@@ -1709,8 +1710,32 @@ namespace Umbraco.Core.Persistence
}
public override object ChangeType(object val) { return val; }
}
public class PocoData
{
/// <summary>
/// Container for a Memory cache object
/// </summary>
/// <remarks>
/// Better to have one memory cache instance than many so it's memory management can be handled more effectively
/// http://stackoverflow.com/questions/8463962/using-multiple-instances-of-memorycache
/// </remarks>
internal class ManagedCache
{
public ObjectCache GetCache()
{
return ObjectCache;
}
static readonly ObjectCache ObjectCache = new MemoryCache("NPoco");
}
public class PocoData
{
//USE ONLY FOR TESTING
internal static bool UseLongKeys = false;
//USE ONLY FOR TESTING - default is one hr
internal static int SlidingExpirationSeconds = 3600;
public static PocoData ForObject(object o, string primaryKeyName)
{
var t = o.GetType();
@@ -1734,7 +1759,7 @@ namespace Umbraco.Core.Persistence
#endif
return ForType(t);
}
static System.Threading.ReaderWriterLockSlim RWLock = new System.Threading.ReaderWriterLockSlim();
public static PocoData ForType(Type t)
{
#if !PETAPOCO_NO_DYNAMIC
@@ -1742,7 +1767,7 @@ namespace Umbraco.Core.Persistence
throw new InvalidOperationException("Can't use dynamic types with this method");
#endif
// Check cache
RWLock.EnterReadLock();
InnerLock.EnterReadLock();
PocoData pd;
try
{
@@ -1751,12 +1776,12 @@ namespace Umbraco.Core.Persistence
}
finally
{
RWLock.ExitReadLock();
InnerLock.ExitReadLock();
}
// Cache it
RWLock.EnterWriteLock();
InnerLock.EnterWriteLock();
try
{
// Check again
@@ -1770,7 +1795,7 @@ namespace Umbraco.Core.Persistence
}
finally
{
RWLock.ExitWriteLock();
InnerLock.ExitWriteLock();
}
return pd;
@@ -1851,224 +1876,237 @@ namespace Umbraco.Core.Persistence
return tc >= TypeCode.SByte && tc <= TypeCode.UInt64;
}
// Create factory function that can convert a IDataReader record into a POCO
public Delegate GetFactory(string sql, string connString, bool ForceDateTimesToUtc, int firstColumn, int countColumns, IDataReader r)
{
// Check cache
var key = string.Format("{0}:{1}:{2}:{3}:{4}", sql, connString, ForceDateTimesToUtc, firstColumn, countColumns);
RWLock.EnterReadLock();
try
{
// Have we already created it?
Delegate factory;
if (PocoFactories.TryGetValue(key, out factory))
return factory;
}
finally
{
RWLock.ExitReadLock();
}
// Take the writer lock
RWLock.EnterWriteLock();
//TODO: It would be nice to remove the irrelevant SQL parts - for a mapping operation anything after the SELECT clause isn't required.
// This would ensure less duplicate entries that get cached, currently both of these queries would be cached even though they are
// returning the same structured data:
// SELECT * FROM MyTable ORDER BY MyColumn
// SELECT * FROM MyTable ORDER BY MyColumn DESC
try
{
string key;
if (UseLongKeys)
{
key = string.Format("{0}:{1}:{2}:{3}:{4}", sql, connString, ForceDateTimesToUtc, firstColumn, countColumns);
}
else
{
//Create a hashed key, we don't want to store so much string data in memory
var combiner = new HashCodeCombiner();
combiner.AddCaseInsensitiveString(sql);
combiner.AddCaseInsensitiveString(connString);
combiner.AddObject(ForceDateTimesToUtc);
combiner.AddInt(firstColumn);
combiner.AddInt(countColumns);
key = combiner.GetCombinedHashCode();
}
// Check again, just in case
Delegate factory;
if (PocoFactories.TryGetValue(key, out factory))
return factory;
var objectCache = _managedCache.GetCache();
// Create the method
var m = new DynamicMethod("petapoco_factory_" + PocoFactories.Count.ToString(), type, new Type[] { typeof(IDataReader) }, true);
var il = m.GetILGenerator();
Func<Delegate> factory = () =>
{
// Create the method
var m = new DynamicMethod("petapoco_factory_" + objectCache.GetCount(), type, new Type[] { typeof(IDataReader) }, true);
var il = m.GetILGenerator();
#if !PETAPOCO_NO_DYNAMIC
if (type == typeof(object))
{
// var poco=new T()
il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes)); // obj
if (type == typeof(object))
{
// var poco=new T()
il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes)); // obj
MethodInfo fnAdd = typeof(IDictionary<string, object>).GetMethod("Add");
MethodInfo fnAdd = typeof(IDictionary<string, object>).GetMethod("Add");
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
var srcType = r.GetFieldType(i);
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
var srcType = r.GetFieldType(i);
il.Emit(OpCodes.Dup); // obj, obj
il.Emit(OpCodes.Ldstr, r.GetName(i)); // obj, obj, fieldname
il.Emit(OpCodes.Dup); // obj, obj
il.Emit(OpCodes.Ldstr, r.GetName(i)); // obj, obj, fieldname
// Get the converter
Func<object, object> converter = null;
if (Database.Mapper != null)
converter = Database.Mapper.GetFromDbConverter(null, srcType);
if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime))
converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
// Get the converter
Func<object, object> converter = null;
if (Database.Mapper != null)
converter = Database.Mapper.GetFromDbConverter(null, srcType);
if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime))
converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
// Setup stack for call to converter
AddConverterToStack(il, converter);
// Setup stack for call to converter
AddConverterToStack(il, converter);
// r[i]
il.Emit(OpCodes.Ldarg_0); // obj, obj, fieldname, converter?, rdr
il.Emit(OpCodes.Ldc_I4, i); // obj, obj, fieldname, converter?, rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // obj, obj, fieldname, converter?, value
// r[i]
il.Emit(OpCodes.Ldarg_0); // obj, obj, fieldname, converter?, rdr
il.Emit(OpCodes.Ldc_I4, i); // obj, obj, fieldname, converter?, rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // obj, obj, fieldname, converter?, value
// Convert DBNull to null
il.Emit(OpCodes.Dup); // obj, obj, fieldname, converter?, value, value
il.Emit(OpCodes.Isinst, typeof(DBNull)); // obj, obj, fieldname, converter?, value, (value or null)
var lblNotNull = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?, value
il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter?
if (converter != null)
il.Emit(OpCodes.Pop); // obj, obj, fieldname,
il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null
if (converter != null)
{
var lblReady = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblReady);
il.MarkLabel(lblNotNull);
il.Emit(OpCodes.Callvirt, fnInvoke);
il.MarkLabel(lblReady);
}
else
{
il.MarkLabel(lblNotNull);
}
// Convert DBNull to null
il.Emit(OpCodes.Dup); // obj, obj, fieldname, converter?, value, value
il.Emit(OpCodes.Isinst, typeof(DBNull)); // obj, obj, fieldname, converter?, value, (value or null)
var lblNotNull = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?, value
il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter?
if (converter != null)
il.Emit(OpCodes.Pop); // obj, obj, fieldname,
il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null
if (converter != null)
{
var lblReady = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblReady);
il.MarkLabel(lblNotNull);
il.Emit(OpCodes.Callvirt, fnInvoke);
il.MarkLabel(lblReady);
}
else
{
il.MarkLabel(lblNotNull);
}
il.Emit(OpCodes.Callvirt, fnAdd);
}
}
else
il.Emit(OpCodes.Callvirt, fnAdd);
}
}
else
#endif
if (type.IsValueType || type == typeof(string) || type == typeof(byte[]))
{
// Do we need to install a converter?
var srcType = r.GetFieldType(0);
var converter = GetConverter(ForceDateTimesToUtc, null, srcType, type);
if (type.IsValueType || type == typeof(string) || type == typeof(byte[]))
{
// Do we need to install a converter?
var srcType = r.GetFieldType(0);
var converter = GetConverter(ForceDateTimesToUtc, null, srcType, type);
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnIsDBNull); // bool
var lblCont = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblCont);
il.Emit(OpCodes.Ldnull); // null
var lblFin = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblFin);
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnIsDBNull); // bool
var lblCont = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblCont);
il.Emit(OpCodes.Ldnull); // null
var lblFin = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblFin);
il.MarkLabel(lblCont);
il.MarkLabel(lblCont);
// Setup stack for call to converter
AddConverterToStack(il, converter);
// Setup stack for call to converter
AddConverterToStack(il, converter);
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnGetValue); // value
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnGetValue); // value
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
il.MarkLabel(lblFin);
il.Emit(OpCodes.Unbox_Any, type); // value converted
}
else
{
// var poco=new T()
il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
il.MarkLabel(lblFin);
il.Emit(OpCodes.Unbox_Any, type); // value converted
}
else
{
// var poco=new T()
il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
// Get the PocoColumn for this db column, ignore if not known
PocoColumn pc;
if (!Columns.TryGetValue(r.GetName(i), out pc))
continue;
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
// Get the PocoColumn for this db column, ignore if not known
PocoColumn pc;
if (!Columns.TryGetValue(r.GetName(i), out pc))
continue;
// Get the source type for this column
var srcType = r.GetFieldType(i);
var dstType = pc.PropertyInfo.PropertyType;
// Get the source type for this column
var srcType = r.GetFieldType(i);
var dstType = pc.PropertyInfo.PropertyType;
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // poco,rdr
il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i
il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool
var lblNext = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, lblNext); // poco
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // poco,rdr
il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i
il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool
var lblNext = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, lblNext); // poco
il.Emit(OpCodes.Dup); // poco,poco
il.Emit(OpCodes.Dup); // poco,poco
// Do we need to install a converter?
var converter = GetConverter(ForceDateTimesToUtc, pc, srcType, dstType);
// Do we need to install a converter?
var converter = GetConverter(ForceDateTimesToUtc, pc, srcType, dstType);
// Fast
bool Handled = false;
if (converter == null)
{
var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) });
if (valuegetter != null
&& valuegetter.ReturnType == srcType
&& (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType)))
{
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, valuegetter); // *,value
// Fast
bool Handled = false;
if (converter == null)
{
var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) });
if (valuegetter != null
&& valuegetter.ReturnType == srcType
&& (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType)))
{
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, valuegetter); // *,value
// Convert to Nullable
if (Nullable.GetUnderlyingType(dstType) != null)
{
il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) }));
}
// Convert to Nullable
if (Nullable.GetUnderlyingType(dstType) != null)
{
il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) }));
}
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
Handled = true;
}
}
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
Handled = true;
}
}
// Not so fast
if (!Handled)
{
// Setup stack for call to converter
AddConverterToStack(il, converter);
// Not so fast
if (!Handled)
{
// Setup stack for call to converter
AddConverterToStack(il, converter);
// "value = rdr.GetValue(i)"
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // *,value
// "value = rdr.GetValue(i)"
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // *,value
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
// Assign it
il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
}
// Assign it
il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
}
il.MarkLabel(lblNext);
}
il.MarkLabel(lblNext);
}
var fnOnLoaded = RecurseInheritedTypes<MethodInfo>(type, (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
if (fnOnLoaded != null)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Callvirt, fnOnLoaded);
}
}
var fnOnLoaded = RecurseInheritedTypes<MethodInfo>(type, (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
if (fnOnLoaded != null)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Callvirt, fnOnLoaded);
}
}
il.Emit(OpCodes.Ret);
il.Emit(OpCodes.Ret);
// return it
var del = m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type));
return del;
};
//lazy usage of AddOrGetExisting ref: http://stackoverflow.com/questions/10559279/how-to-deal-with-costly-building-operations-using-memorycache/15894928#15894928
var newValue = new Lazy<Delegate>(factory);
// the line belows returns existing item or adds the new value if it doesn't exist
var value = (Lazy<Delegate>)objectCache.AddOrGetExisting(key, newValue, new CacheItemPolicy
{
//sliding expiration of 1 hr, if the same key isn't used in this
// timeframe it will be removed from the cache
SlidingExpiration = new TimeSpan(0, 0, SlidingExpirationSeconds)
});
return (value ?? newValue).Value; // Lazy<T> handles the locking itself
// Cache it, return it
var del = m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type));
PocoFactories.Add(key, del);
return del;
}
finally
{
RWLock.ExitWriteLock();
}
}
private static void AddConverterToStack(ILGenerator il, Func<object, object> converter)
@@ -2144,7 +2182,7 @@ namespace Umbraco.Core.Persistence
return default(T);
}
ManagedCache _managedCache = new ManagedCache();
static Dictionary<Type, PocoData> m_PocoDatas = new Dictionary<Type, PocoData>();
static List<Func<object, object>> m_Converters = new List<Func<object, object>>();
static MethodInfo fnGetValue = typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
@@ -2156,7 +2194,46 @@ namespace Umbraco.Core.Persistence
public string[] QueryColumns { get; private set; }
public TableInfo TableInfo { get; private set; }
public Dictionary<string, PocoColumn> Columns { get; private set; }
Dictionary<string, Delegate> PocoFactories = new Dictionary<string, Delegate>();
static System.Threading.ReaderWriterLockSlim InnerLock = new System.Threading.ReaderWriterLockSlim();
/// <summary>
/// Returns a report of the current cache being utilized by PetaPoco
/// </summary>
/// <returns></returns>
public static string PrintDebugCacheReport(out double totalBytes, out IEnumerable<string> allKeys)
{
var managedCache = new ManagedCache();
var sb = new StringBuilder();
sb.AppendLine("m_PocoDatas:");
foreach (var pocoData in m_PocoDatas)
{
sb.AppendFormat("\t{0}\n", pocoData.Key);
sb.AppendFormat("\t\tTable:{0} - Col count:{1}\n", pocoData.Value.TableInfo.TableName, pocoData.Value.QueryColumns.Length);
}
var cache = managedCache.GetCache();
allKeys = cache.Select(x => x.Key).ToArray();
sb.AppendFormat("\tTotal Poco data count:{0}\n", allKeys.Count());
var keys = string.Join("", cache.Select(x => x.Key));
//Bytes in .Net are stored as utf-16 = unicode little endian
totalBytes = Encoding.Unicode.GetByteCount(keys);
sb.AppendFormat("\tTotal byte for keys:{0}\n", totalBytes);
sb.AppendLine("\tAll Poco cache items:");
foreach (var item in cache)
{
sb.AppendFormat("\t\t Key -> {0}\n", item.Key);
sb.AppendFormat("\t\t Value -> {0}\n", item.Value);
}
sb.AppendLine("-------------------END REPORT------------------------");
return sb.ToString();
}
}
@@ -221,6 +221,7 @@ namespace Umbraco.Core.Persistence
if (overwrite && tableExist)
{
db.DropTable(tableName);
tableExist = false;
}
if (tableExist == false)
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Umbraco.Core.Persistence.Querying;
@@ -25,7 +26,7 @@ namespace Umbraco.Core.Persistence
var expresionist = new PocoToSqlExpressionHelper<T>();
string whereExpression = expresionist.Visit(predicate);
return sql.Where(whereExpression);
return sql.Where(whereExpression, expresionist.GetSqlParameters());
}
public static Sql OrderBy<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember)
@@ -125,5 +126,10 @@ namespace Umbraco.Core.Persistence
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(rightColumnName));
return sql.On(onClause);
}
public static Sql OrderByDescending(this Sql sql, params object[] columns)
{
return sql.Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x + " DESC").ToArray())));
}
}
}
@@ -1,28 +1,562 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
internal abstract class BaseExpressionHelper<T> : BaseExpressionHelper
{
protected abstract string VisitMemberAccess(MemberExpression m);
protected internal virtual string Visit(Expression exp)
{
if (exp == null) return string.Empty;
switch (exp.NodeType)
{
case ExpressionType.Lambda:
return VisitLambda(exp as LambdaExpression);
case ExpressionType.MemberAccess:
return VisitMemberAccess(exp as MemberExpression);
case ExpressionType.Constant:
return VisitConstant(exp as ConstantExpression);
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.ExclusiveOr:
return VisitBinary(exp as BinaryExpression);
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.ArrayLength:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
return VisitUnary(exp as UnaryExpression);
case ExpressionType.Parameter:
return VisitParameter(exp as ParameterExpression);
case ExpressionType.Call:
return VisitMethodCall(exp as MethodCallExpression);
case ExpressionType.New:
return VisitNew(exp as NewExpression);
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
return VisitNewArray(exp as NewArrayExpression);
default:
return exp.ToString();
}
}
protected virtual string VisitLambda(LambdaExpression lambda)
{
if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
var m = lambda.Body as MemberExpression;
if (m.Expression != null)
{
//This deals with members that are boolean (i.e. x => IsTrashed )
string r = VisitMemberAccess(m);
SqlParameters.Add(true);
return string.Format("{0} = @{1}", r, SqlParameters.Count - 1);
//return string.Format("{0}={1}", r, GetQuotedTrueValue());
}
}
return Visit(lambda.Body);
}
protected virtual string VisitBinary(BinaryExpression b)
{
string left, right;
var operand = BindOperant(b.NodeType);
if (operand == "AND" || operand == "OR")
{
MemberExpression m = b.Left as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
SqlParameters.Add(1);
left = string.Format("{0} = @{1}", r, SqlParameters.Count - 1);
//left = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
left = Visit(b.Left);
}
m = b.Right as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
SqlParameters.Add(1);
right = string.Format("{0} = @{1}", r, SqlParameters.Count - 1);
//right = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
right = Visit(b.Right);
}
}
else
{
left = Visit(b.Left);
right = Visit(b.Right);
}
if (operand == "=" && right == "null") operand = "is";
else if (operand == "<>" && right == "null") operand = "is not";
else if (operand == "=" || operand == "<>")
{
//if (IsTrueExpression(right)) right = GetQuotedTrueValue();
//else if (IsFalseExpression(right)) right = GetQuotedFalseValue();
//if (IsTrueExpression(left)) left = GetQuotedTrueValue();
//else if (IsFalseExpression(left)) left = GetQuotedFalseValue();
}
switch (operand)
{
case "MOD":
case "COALESCE":
return string.Format("{0}({1},{2})", operand, left, right);
default:
return left + " " + operand + " " + right;
}
}
protected virtual List<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
{
var list = new List<Object>();
for (int i = 0, n = original.Count; i < n; i++)
{
if (original[i].NodeType == ExpressionType.NewArrayInit ||
original[i].NodeType == ExpressionType.NewArrayBounds)
{
list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression));
}
else
list.Add(Visit(original[i]));
}
return list;
}
protected virtual string VisitNew(NewExpression nex)
{
// TODO : check !
var member = Expression.Convert(nex, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
try
{
var getter = lambda.Compile();
object o = getter();
SqlParameters.Add(o);
return string.Format("@{0}", SqlParameters.Count - 1);
//return GetQuotedValue(o, o.GetType());
}
catch (InvalidOperationException)
{
// FieldName ?
List<Object> exprs = VisitExpressionList(nex.Arguments);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.AppendFormat("{0}{1}",
r.Length > 0 ? "," : "",
e);
}
return r.ToString();
}
}
protected virtual string VisitParameter(ParameterExpression p)
{
return p.Name;
}
protected virtual string VisitConstant(ConstantExpression c)
{
if (c.Value == null)
return "null";
SqlParameters.Add(c.Value);
return string.Format("@{0}", SqlParameters.Count - 1);
//if (c.Value is bool)
//{
// object o = GetQuotedValue(c.Value, c.Value.GetType());
// return string.Format("({0}={1})", GetQuotedTrueValue(), o);
//}
//return GetQuotedValue(c.Value, c.Value.GetType());
}
protected virtual string VisitUnary(UnaryExpression u)
{
switch (u.NodeType)
{
case ExpressionType.Not:
var o = Visit(u.Operand);
//use a Not equal operator instead of <> since we don't know that <> works in all sql servers
switch (u.Operand.NodeType)
{
case ExpressionType.MemberAccess:
//In this case it wil be a false property , i.e. x => !Trashed
SqlParameters.Add(true);
return string.Format("NOT ({0} = @0)", o);
default:
//In this case it could be anything else, such as: x => !x.Path.StartsWith("-20")
return string.Format("NOT ({0})", o);
}
default:
return Visit(u.Operand);
}
}
protected virtual string VisitNewArray(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.Append(r.Length > 0 ? "," + e : e);
}
return r.ToString();
}
protected virtual List<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
return exprs;
}
protected virtual string BindOperant(ExpressionType e)
{
switch (e)
{
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.AndAlso:
return "AND";
case ExpressionType.OrElse:
return "OR";
case ExpressionType.Add:
return "+";
case ExpressionType.Subtract:
return "-";
case ExpressionType.Multiply:
return "*";
case ExpressionType.Divide:
return "/";
case ExpressionType.Modulo:
return "MOD";
case ExpressionType.Coalesce:
return "COALESCE";
default:
return e.ToString();
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
//Here's what happens with a MethodCallExpression:
// If a method is called that contains a single argument,
// then m.Object is the object on the left hand side of the method call, example:
// x.Path.StartsWith(content.Path)
// m.Object = x.Path
// and m.Arguments.Length == 1, therefor m.Arguments[0] == content.Path
// If a method is called that contains multiple arguments, then m.Object == null and the
// m.Arguments collection contains the left hand side of the method call, example:
// x.Path.SqlStartsWith(content.Path, TextColumnType.NVarchar)
// m.Object == null
// m.Arguments.Length == 3, therefor, m.Arguments[0] == x.Path, m.Arguments[1] == content.Path, m.Arguments[2] == TextColumnType.NVarchar
// So, we need to cater for these scenarios.
var objectForMethod = m.Object ?? m.Arguments[0];
var visitedObjectForMethod = Visit(objectForMethod);
var methodArgs = m.Object == null
? m.Arguments.Skip(1).ToArray()
: m.Arguments.ToArray();
switch (m.Method.Name)
{
case "ToString":
SqlParameters.Add(objectForMethod.ToString());
return string.Format("@{0}", SqlParameters.Count - 1);
case "ToUpper":
return string.Format("upper({0})", visitedObjectForMethod);
case "ToLower":
return string.Format("lower({0})", visitedObjectForMethod);
case "SqlWildcard":
case "StartsWith":
case "EndsWith":
case "Contains":
case "Equals":
case "SqlStartsWith":
case "SqlEndsWith":
case "SqlContains":
case "SqlEquals":
case "InvariantStartsWith":
case "InvariantEndsWith":
case "InvariantContains":
case "InvariantEquals":
string compareValue;
if (methodArgs[0].NodeType != ExpressionType.Constant)
{
//This occurs when we are getting a value from a non constant such as: x => x.Path.StartsWith(content.Path)
// So we'll go get the value:
var member = Expression.Convert(methodArgs[0], typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
compareValue = getter().ToString();
}
else
{
compareValue = methodArgs[0].ToString();
}
//special case, if it is 'Contains' and the member that Contains is being called on is not a string, then
// we should be doing an 'In' clause - but we currently do not support this
if (methodArgs[0].Type != typeof(string) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(methodArgs[0].Type))
{
throw new NotSupportedException("An array Contains method is not supported");
}
//default column type
var colType = TextColumnType.NVarchar;
//then check if the col type argument has been passed to the current method (this will be the case for methods like
// SqlContains and other Sql methods)
if (methodArgs.Length > 1)
{
var colTypeArg = methodArgs.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType));
if (colTypeArg != null)
{
colType = (TextColumnType)((ConstantExpression)colTypeArg).Value;
}
}
return HandleStringComparison(visitedObjectForMethod, compareValue, m.Method.Name, colType);
//case "Substring":
// var startIndex = Int32.Parse(args[0].ToString()) + 1;
// if (args.Count == 2)
// {
// var length = Int32.Parse(args[1].ToString());
// return string.Format("substring({0} from {1} for {2})",
// r,
// startIndex,
// length);
// }
// else
// return string.Format("substring({0} from {1})",
// r,
// startIndex);
//case "Round":
//case "Floor":
//case "Ceiling":
//case "Coalesce":
//case "Abs":
//case "Sum":
// return string.Format("{0}({1}{2})",
// m.Method.Name,
// r,
// args.Count == 1 ? string.Format(",{0}", args[0]) : "");
//case "Concat":
// var s = new StringBuilder();
// foreach (Object e in args)
// {
// s.AppendFormat(" || {0}", e);
// }
// return string.Format("{0}{1}", r, s);
//case "In":
// var member = Expression.Convert(m.Arguments[0], typeof(object));
// var lambda = Expression.Lambda<Func<object>>(member);
// var getter = lambda.Compile();
// var inArgs = (object[])getter();
// var sIn = new StringBuilder();
// foreach (var e in inArgs)
// {
// SqlParameters.Add(e);
// sIn.AppendFormat("{0}{1}",
// sIn.Length > 0 ? "," : "",
// string.Format("@{0}", SqlParameters.Count - 1));
// //sIn.AppendFormat("{0}{1}",
// // sIn.Length > 0 ? "," : "",
// // GetQuotedValue(e, e.GetType()));
// }
// return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString());
//case "Desc":
// return string.Format("{0} DESC", r);
//case "Alias":
//case "As":
// return string.Format("{0} As {1}", r,
// GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString()))));
default:
throw new ArgumentOutOfRangeException("No logic supported for " + m.Method.Name);
//var s2 = new StringBuilder();
//foreach (Object e in args)
//{
// s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType()));
//}
//return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString());
}
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);
}
public virtual string GetQuotedColumnName(string columnName)
{
return string.Format("\"{0}\"", columnName);
}
public virtual string GetQuotedName(string name)
{
return string.Format("\"{0}\"", name);
}
//private string GetQuotedTrueValue()
//{
// return GetQuotedValue(true, typeof(bool));
//}
//private string GetQuotedFalseValue()
//{
// return GetQuotedValue(false, typeof(bool));
//}
//public virtual string GetQuotedValue(object value, Type fieldType)
//{
// return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue);
//}
//private string GetTrueExpression()
//{
// object o = GetQuotedTrueValue();
// return string.Format("({0}={1})", o, o);
//}
//private string GetFalseExpression()
//{
// return string.Format("({0}={1})",
// GetQuotedTrueValue(),
// GetQuotedFalseValue());
//}
//private bool IsTrueExpression(string exp)
//{
// return (exp == GetTrueExpression());
//}
//private bool IsFalseExpression(string exp)
//{
// return (exp == GetFalseExpression());
//}
}
/// <summary>
/// Logic that is shared with the expression helpers
/// </summary>
internal class BaseExpressionHelper
internal class BaseExpressionHelper
{
protected List<object> SqlParameters = new List<object>();
public object[] GetSqlParameters()
{
return SqlParameters.ToArray();
}
protected string HandleStringComparison(string col, string val, string verb, TextColumnType columnType)
{
switch (verb)
{
case "SqlWildcard":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, RemoveQuote(val), columnType);
SqlParameters.Add(RemoveQuote(val));
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType);
case "Equals":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, RemoveQuote(val), columnType);
SqlParameters.Add(RemoveQuote(val));
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, SqlParameters.Count - 1, columnType);
case "StartsWith":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, RemoveQuote(val), columnType);
SqlParameters.Add(string.Format("{0}{1}",
RemoveQuote(val),
SqlSyntaxContext.SqlSyntaxProvider.GetWildcardPlaceholder()));
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType);
case "EndsWith":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, RemoveQuote(val), columnType);
SqlParameters.Add(string.Format("{0}{1}",
SqlSyntaxContext.SqlSyntaxProvider.GetWildcardPlaceholder(),
RemoveQuote(val)));
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType);
case "Contains":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, RemoveQuote(val), columnType);
SqlParameters.Add(string.Format("{0}{1}{0}",
SqlSyntaxContext.SqlSyntaxProvider.GetWildcardPlaceholder(),
RemoveQuote(val)));
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType);
case "InvariantEquals":
case "SqlEquals":
//recurse
@@ -44,61 +578,54 @@ namespace Umbraco.Core.Persistence.Querying
}
}
public virtual string GetQuotedValue(object value, Type fieldType, Func<object, string> escapeCallback = null, Func<Type, bool> shouldQuoteCallback = null)
{
if (value == null) return "NULL";
//public virtual string GetQuotedValue(object value, Type fieldType, Func<object, string> escapeCallback = null, Func<Type, bool> shouldQuoteCallback = null)
//{
// if (value == null) return "NULL";
if (escapeCallback == null)
{
escapeCallback = EscapeParam;
}
if (shouldQuoteCallback == null)
{
shouldQuoteCallback = ShouldQuoteValue;
}
// if (escapeCallback == null)
// {
// escapeCallback = EscapeParam;
// }
// if (shouldQuoteCallback == null)
// {
// shouldQuoteCallback = ShouldQuoteValue;
// }
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
{
//if (TypeSerializer.CanCreateFromString(fieldType))
//{
// return "'" + escapeCallback(TypeSerializer.SerializeToString(value)) + "'";
//}
// if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
// {
// //if (TypeSerializer.CanCreateFromString(fieldType))
// //{
// // return "'" + escapeCallback(TypeSerializer.SerializeToString(value)) + "'";
// //}
throw new NotSupportedException(
string.Format("Property of type: {0} is not supported", fieldType.FullName));
}
// throw new NotSupportedException(
// string.Format("Property of type: {0} is not supported", fieldType.FullName));
// }
if (fieldType == typeof(int))
return ((int)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(int))
// return ((int)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(float))
return ((float)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(float))
// return ((float)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(double))
return ((double)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(double))
// return ((double)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(decimal))
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(decimal))
// return ((decimal)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(DateTime))
{
return "'" + escapeCallback(((DateTime)value).ToIsoString()) + "'";
}
// if (fieldType == typeof(DateTime))
// {
// return "'" + escapeCallback(((DateTime)value).ToIsoString()) + "'";
// }
if (fieldType == typeof(bool))
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
// if (fieldType == typeof(bool))
// return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
return shouldQuoteCallback(fieldType)
? "'" + escapeCallback(value) + "'"
: value.ToString();
}
public virtual string EscapeParam(object paramValue)
{
return paramValue == null
? string.Empty
: SqlSyntaxContext.SqlSyntaxProvider.EscapeString(paramValue.ToString());
}
// return shouldQuoteCallback(fieldType)
// ? "'" + escapeCallback(value) + "'"
// : value.ToString();
//}
public virtual bool ShouldQuoteValue(Type fieldType)
{
@@ -107,16 +634,12 @@ namespace Umbraco.Core.Persistence.Querying
protected virtual string RemoveQuote(string exp)
{
if (exp.StartsWith("'") && exp.EndsWith("'"))
{
exp = exp.Remove(0, 1);
exp = exp.Remove(exp.Length - 1, 1);
}
return exp;
}
protected virtual string RemoveQuoteFromAlias(string exp)
{
//if (exp.StartsWith("'") && exp.EndsWith("'"))
//{
// exp = exp.Remove(0, 1);
// exp = exp.Remove(exp.Length - 1, 1);
//}
//return exp;
if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
&&
@@ -127,5 +650,18 @@ namespace Umbraco.Core.Persistence.Querying
}
return exp;
}
//protected virtual string RemoveQuoteFromAlias(string exp)
//{
// if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
// &&
// (exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
// {
// exp = exp.Remove(0, 1);
// exp = exp.Remove(exp.Length - 1, 1);
// }
// return exp;
//}
}
}
@@ -1,10 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Umbraco.Core.Persistence.Querying
{
/// <summary>
/// Represents a query for building Linq translatable SQL queries
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IQuery<T>
{
/// <summary>
/// Adds a where clause to the query
/// </summary>
/// <param name="predicate"></param>
/// <returns>This instance so calls to this method are chainable</returns>
IQuery<T> Where(Expression<Func<T, bool>> predicate);
/// <summary>
/// Returns all translated where clauses and their sql parameters
/// </summary>
/// <returns></returns>
IEnumerable<Tuple<string, object[]>> GetWhereClauses();
}
}
@@ -10,149 +10,21 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
internal class ModelToSqlExpressionHelper<T> : BaseExpressionHelper
internal class ModelToSqlExpressionHelper<T> : BaseExpressionHelper<T>
{
private string sep = " ";
private BaseMapper _mapper;
private readonly BaseMapper _mapper;
public ModelToSqlExpressionHelper()
{
_mapper = MappingResolver.Current.ResolveMapperByType(typeof(T));
}
protected internal virtual string Visit(Expression exp)
protected override string VisitMemberAccess(MemberExpression m)
{
if (exp == null) return string.Empty;
switch (exp.NodeType)
{
case ExpressionType.Lambda:
return VisitLambda(exp as LambdaExpression);
case ExpressionType.MemberAccess:
return VisitMemberAccess(exp as MemberExpression);
case ExpressionType.Constant:
return VisitConstant(exp as ConstantExpression);
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.ExclusiveOr:
return VisitBinary(exp as BinaryExpression);
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.ArrayLength:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
return VisitUnary(exp as UnaryExpression);
case ExpressionType.Parameter:
return VisitParameter(exp as ParameterExpression);
case ExpressionType.Call:
return VisitMethodCall(exp as MethodCallExpression);
case ExpressionType.New:
return VisitNew(exp as NewExpression);
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
return VisitNewArray(exp as NewArrayExpression);
default:
return exp.ToString();
}
}
protected virtual string VisitLambda(LambdaExpression lambda)
{
if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
{
MemberExpression m = lambda.Body as MemberExpression;
if (m.Expression != null)
{
string r = VisitMemberAccess(m);
return string.Format("{0}={1}", r, GetQuotedTrueValue());
}
}
return Visit(lambda.Body);
}
protected virtual string VisitBinary(BinaryExpression b)
{
string left, right;
var operand = BindOperant(b.NodeType); //sep= " " ??
if (operand == "AND" || operand == "OR")
{
MemberExpression m = b.Left as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
left = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
left = Visit(b.Left);
}
m = b.Right as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
right = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
right = Visit(b.Right);
}
}
else
{
left = Visit(b.Left);
right = Visit(b.Right);
}
if (operand == "=" && right == "null") operand = "is";
else if (operand == "<>" && right == "null") operand = "is not";
else if (operand == "=" || operand == "<>")
{
if (IsTrueExpression(right)) right = GetQuotedTrueValue();
else if (IsFalseExpression(right)) right = GetQuotedFalseValue();
if (IsTrueExpression(left)) left = GetQuotedTrueValue();
else if (IsFalseExpression(left)) left = GetQuotedFalseValue();
}
switch (operand)
{
case "MOD":
case "COALESCE":
return string.Format("{0}({1},{2})", operand, left, right);
default:
return left + sep + operand + sep + right;
}
}
protected virtual string VisitMemberAccess(MemberExpression m)
{
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(T))
if (m.Expression != null &&
m.Expression.NodeType == ExpressionType.Parameter
&& m.Expression.Type == typeof(T))
{
var field = _mapper.Map(m.Member.Name);
return field;
@@ -168,324 +40,18 @@ namespace Umbraco.Core.Persistence.Querying
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o != null ? o.GetType() : null);
SqlParameters.Add(o);
return string.Format("@{0}", SqlParameters.Count - 1);
//return GetQuotedValue(o, o != null ? o.GetType() : null);
}
protected virtual string VisitNew(NewExpression nex)
{
// TODO : check !
var member = Expression.Convert(nex, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
try
{
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o.GetType());
}
catch (System.InvalidOperationException)
{ // FieldName ?
List<Object> exprs = VisitExpressionList(nex.Arguments);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.AppendFormat("{0}{1}", r.Length > 0 ? "," : "", e);
}
return r.ToString();
}
}
protected virtual string VisitParameter(ParameterExpression p)
{
return p.Name;
}
protected virtual string VisitConstant(ConstantExpression c)
{
if (c.Value == null)
return "null";
if (c.Value is bool)
{
object o = GetQuotedValue(c.Value, c.Value.GetType());
return string.Format("({0}={1})", GetQuotedTrueValue(), o);
}
return GetQuotedValue(c.Value, c.Value.GetType());
}
protected virtual string VisitUnary(UnaryExpression u)
{
switch (u.NodeType)
{
case ExpressionType.Not:
string o = Visit(u.Operand);
if (IsFieldName(o)) o = o + "=" + GetQuotedValue(true, typeof(bool));
return "NOT (" + o + ")";
default:
return Visit(u.Operand);
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
List<Object> args = this.VisitExpressionList(m.Arguments);
Object r;
if (m.Object != null)
r = Visit(m.Object);
else
{
r = args[0];
args.RemoveAt(0);
}
switch (m.Method.Name)
{
case "ToUpper":
return string.Format("upper({0})", r);
case "ToLower":
return string.Format("lower({0})", r);
case "SqlWildcard":
case "StartsWith":
case "EndsWith":
case "Contains":
case "Equals":
case "SqlStartsWith":
case "SqlEndsWith":
case "SqlContains":
case "SqlEquals":
case "InvariantStartsWith":
case "InvariantEndsWith":
case "InvariantContains":
case "InvariantEquals":
//default
var colType = TextColumnType.NVarchar;
//then check if this arg has been passed in
if (m.Arguments.Count > 1)
{
var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType));
if (colTypeArg != null)
{
colType = (TextColumnType) ((ConstantExpression) colTypeArg).Value;
}
}
return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType);
case "Substring":
var startIndex = Int32.Parse(args[0].ToString()) + 1;
if (args.Count == 2)
{
var length = Int32.Parse(args[1].ToString());
return string.Format("substring({0} from {1} for {2})",
r,
startIndex,
length);
}
else
return string.Format("substring({0} from {1})",
r,
startIndex);
case "Round":
case "Floor":
case "Ceiling":
case "Coalesce":
case "Abs":
case "Sum":
return string.Format("{0}({1}{2})",
m.Method.Name,
r,
args.Count == 1 ? string.Format(",{0}", args[0]) : "");
case "Concat":
var s = new StringBuilder();
foreach (Object e in args)
{
s.AppendFormat(" || {0}", e);
}
return string.Format("{0}{1}", r, s.ToString());
case "In":
var member = Expression.Convert(m.Arguments[1], typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
var inArgs = getter() as object[];
var sIn = new StringBuilder();
foreach (Object e in inArgs)
{
if (e.GetType().ToString() != "System.Collections.Generic.List`1[System.Object]")
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(e, e.GetType()));
}
else
{
var listArgs = e as IList<Object>;
foreach (Object el in listArgs)
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(el, el.GetType()));
}
}
}
return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString());
case "Desc":
return string.Format("{0} DESC", r);
case "Alias":
case "As":
return string.Format("{0} As {1}", r,
GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString()))));
case "ToString":
return r.ToString();
default:
var s2 = new StringBuilder();
foreach (Object e in args)
{
s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType()));
}
return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString());
}
}
protected virtual List<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
{
var list = new List<Object>();
for (int i = 0, n = original.Count; i < n; i++)
{
if (original[i].NodeType == ExpressionType.NewArrayInit ||
original[i].NodeType == ExpressionType.NewArrayBounds)
{
list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression));
}
else
list.Add(Visit(original[i]));
}
return list;
}
protected virtual string VisitNewArray(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.Append(r.Length > 0 ? "," + e : e);
}
return r.ToString();
}
protected virtual List<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
return exprs;
}
protected virtual string BindOperant(ExpressionType e)
{
switch (e)
{
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.AndAlso:
return "AND";
case ExpressionType.OrElse:
return "OR";
case ExpressionType.Add:
return "+";
case ExpressionType.Subtract:
return "-";
case ExpressionType.Multiply:
return "*";
case ExpressionType.Divide:
return "/";
case ExpressionType.Modulo:
return "MOD";
case ExpressionType.Coalesce:
return "COALESCE";
default:
return e.ToString();
}
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);
}
public virtual string GetQuotedColumnName(string columnName)
{
return string.Format("\"{0}\"", columnName);
}
public virtual string GetQuotedName(string name)
{
return string.Format("\"{0}\"", name);
}
private string GetQuotedTrueValue()
{
return GetQuotedValue(true, typeof(bool));
}
private string GetQuotedFalseValue()
{
return GetQuotedValue(false, typeof(bool));
}
public virtual string GetQuotedValue(object value, Type fieldType)
{
return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue);
}
private string GetTrueExpression()
{
object o = GetQuotedTrueValue();
return string.Format("({0}={1})", o, o);
}
private string GetFalseExpression()
{
return string.Format("({0}={1})",
GetQuotedTrueValue(),
GetQuotedFalseValue());
}
private bool IsTrueExpression(string exp)
{
return (exp == GetTrueExpression());
}
private bool IsFalseExpression(string exp)
{
return (exp == GetFalseExpression());
}
protected bool IsFieldName(string quotedExp)
{
//Not entirely sure this is reliable, but its better then simply returning true
return quotedExp.LastIndexOf("'", StringComparison.InvariantCultureIgnoreCase) + 1 != quotedExp.Length;
}
//protected bool IsFieldName(string quotedExp)
//{
// //Not entirely sure this is reliable, but its better then simply returning true
// return quotedExp.LastIndexOf("'", StringComparison.InvariantCultureIgnoreCase) + 1 != quotedExp.Length;
//}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@@ -8,159 +9,28 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
internal class PocoToSqlExpressionHelper<T> : BaseExpressionHelper
internal class PocoToSqlExpressionHelper<T> : BaseExpressionHelper<T>
{
private string sep = " ";
private Database.PocoData pd;
private readonly Database.PocoData _pd;
public PocoToSqlExpressionHelper()
{
pd = new Database.PocoData(typeof(T));
_pd = new Database.PocoData(typeof(T));
}
protected internal virtual string Visit(Expression exp)
{
if (exp == null) return string.Empty;
switch (exp.NodeType)
{
case ExpressionType.Lambda:
return VisitLambda(exp as LambdaExpression);
case ExpressionType.MemberAccess:
return VisitMemberAccess(exp as MemberExpression);
case ExpressionType.Constant:
return VisitConstant(exp as ConstantExpression);
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.ExclusiveOr:
return VisitBinary(exp as BinaryExpression);
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.ArrayLength:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
return VisitUnary(exp as UnaryExpression);
case ExpressionType.Parameter:
return VisitParameter(exp as ParameterExpression);
case ExpressionType.Call:
return VisitMethodCall(exp as MethodCallExpression);
case ExpressionType.New:
return VisitNew(exp as NewExpression);
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
return VisitNewArray(exp as NewArrayExpression);
default:
return exp.ToString();
}
}
protected virtual string VisitLambda(LambdaExpression lambda)
{
if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
{
MemberExpression m = lambda.Body as MemberExpression;
if (m.Expression != null)
{
string r = VisitMemberAccess(m);
return string.Format("{0}={1}", r, GetQuotedTrueValue());
}
}
return Visit(lambda.Body);
}
protected virtual string VisitBinary(BinaryExpression b)
{
string left, right;
var operand = BindOperant(b.NodeType); //sep= " " ??
if (operand == "AND" || operand == "OR")
{
MemberExpression m = b.Left as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
left = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
left = Visit(b.Left);
}
m = b.Right as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
right = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
right = Visit(b.Right);
}
}
else
{
left = Visit(b.Left);
right = Visit(b.Right);
}
if (operand == "=" && right == "null") operand = "is";
else if (operand == "<>" && right == "null") operand = "is not";
else if (operand == "=" || operand == "<>")
{
if (IsTrueExpression(right)) right = GetQuotedTrueValue();
else if (IsFalseExpression(right)) right = GetQuotedFalseValue();
if (IsTrueExpression(left)) left = GetQuotedTrueValue();
else if (IsFalseExpression(left)) left = GetQuotedFalseValue();
}
switch (operand)
{
case "MOD":
case "COALESCE":
return string.Format("{0}({1},{2})", operand, left, right);
default:
return left + sep + operand + sep + right;
}
}
protected virtual string VisitMemberAccess(MemberExpression m)
protected override string VisitMemberAccess(MemberExpression m)
{
if (m.Expression != null &&
m.Expression.NodeType == ExpressionType.Parameter
&& m.Expression.Type == typeof(T))
{
string field = GetFieldName(pd, m.Member.Name);
string field = GetFieldName(_pd, m.Member.Name);
return field;
}
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert)
{
string field = GetFieldName(pd, m.Member.Name);
string field = GetFieldName(_pd, m.Member.Name);
return field;
}
@@ -168,303 +38,14 @@ namespace Umbraco.Core.Persistence.Querying
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o != null ? o.GetType() : null);
SqlParameters.Add(o);
return string.Format("@{0}", SqlParameters.Count - 1);
//return GetQuotedValue(o, o != null ? o.GetType() : null);
}
protected virtual string VisitNew(NewExpression nex)
{
// TODO : check !
var member = Expression.Convert(nex, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
try
{
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o.GetType());
}
catch (System.InvalidOperationException)
{ // FieldName ?
List<Object> exprs = VisitExpressionList(nex.Arguments);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.AppendFormat("{0}{1}",
r.Length > 0 ? "," : "",
e);
}
return r.ToString();
}
}
protected virtual string VisitParameter(ParameterExpression p)
{
return p.Name;
}
protected virtual string VisitConstant(ConstantExpression c)
{
if (c.Value == null)
return "null";
else if (c.Value.GetType() == typeof(bool))
{
object o = GetQuotedValue(c.Value, c.Value.GetType());
return string.Format("({0}={1})", GetQuotedTrueValue(), o);
}
else
return GetQuotedValue(c.Value, c.Value.GetType());
}
protected virtual string VisitUnary(UnaryExpression u)
{
switch (u.NodeType)
{
case ExpressionType.Not:
string o = Visit(u.Operand);
if (IsFieldName(o)) o = o + "=" + GetQuotedValue(true, typeof(bool));
return "NOT (" + o + ")";
default:
return Visit(u.Operand);
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
List<Object> args = this.VisitExpressionList(m.Arguments);
Object r;
if (m.Object != null)
r = Visit(m.Object);
else
{
r = args[0];
args.RemoveAt(0);
}
//TODO: We should probably add the same logic we've done for ModelToSqlExpressionHelper with checking for:
// InvariantStartsWith, InvariantEndsWith, SqlWildcard, etc...
// since we should be able to easily handle that with the Poco objects too.
switch (m.Method.Name)
{
case "ToUpper":
return string.Format("upper({0})", r);
case "ToLower":
return string.Format("lower({0})", r);
case "SqlWildcard":
case "StartsWith":
case "EndsWith":
case "Contains":
case "Equals":
case "SqlStartsWith":
case "SqlEndsWith":
case "SqlContains":
case "SqlEquals":
case "InvariantStartsWith":
case "InvariantEndsWith":
case "InvariantContains":
case "InvariantEquals":
//default
var colType = TextColumnType.NVarchar;
//then check if this arg has been passed in
if (m.Arguments.Count > 1)
{
var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType));
if (colTypeArg != null)
{
colType = (TextColumnType)((ConstantExpression)colTypeArg).Value;
}
}
return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType);
case "Substring":
var startIndex = Int32.Parse(args[0].ToString()) + 1;
if (args.Count == 2)
{
var length = Int32.Parse(args[1].ToString());
return string.Format("substring({0} from {1} for {2})",
r,
startIndex,
length);
}
else
return string.Format("substring({0} from {1})",
r,
startIndex);
case "Round":
case "Floor":
case "Ceiling":
case "Coalesce":
case "Abs":
case "Sum":
return string.Format("{0}({1}{2})",
m.Method.Name,
r,
args.Count == 1 ? string.Format(",{0}", args[0]) : "");
case "Concat":
var s = new StringBuilder();
foreach (Object e in args)
{
s.AppendFormat(" || {0}", e);
}
return string.Format("{0}{1}", r, s.ToString());
case "In":
var member = Expression.Convert(m.Arguments[1], typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
var inArgs = getter() as object[];
var sIn = new StringBuilder();
foreach (Object e in inArgs)
{
if (e.GetType().ToString() != "System.Collections.Generic.List`1[System.Object]")
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(e, e.GetType()));
}
else
{
var listArgs = e as IList<Object>;
foreach (Object el in listArgs)
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(el, el.GetType()));
}
}
}
return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString());
case "Desc":
return string.Format("{0} DESC", r);
case "Alias":
case "As":
return string.Format("{0} As {1}", r,
GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString()))));
case "ToString":
return r.ToString();
default:
var s2 = new StringBuilder();
foreach (Object e in args)
{
s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType()));
}
return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString());
}
}
protected virtual List<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
{
var list = new List<Object>();
for (int i = 0, n = original.Count; i < n; i++)
{
if (original[i].NodeType == ExpressionType.NewArrayInit ||
original[i].NodeType == ExpressionType.NewArrayBounds)
{
list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression));
}
else
list.Add(Visit(original[i]));
}
return list;
}
protected virtual string VisitNewArray(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.Append(r.Length > 0 ? "," + e : e);
}
return r.ToString();
}
protected virtual List<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
return exprs;
}
protected virtual string BindOperant(ExpressionType e)
{
switch (e)
{
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.AndAlso:
return "AND";
case ExpressionType.OrElse:
return "OR";
case ExpressionType.Add:
return "+";
case ExpressionType.Subtract:
return "-";
case ExpressionType.Multiply:
return "*";
case ExpressionType.Divide:
return "/";
case ExpressionType.Modulo:
return "MOD";
case ExpressionType.Coalesce:
return "COALESCE";
default:
return e.ToString();
}
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);
}
public virtual string GetQuotedColumnName(string columnName)
{
return string.Format("\"{0}\"", columnName);
}
public virtual string GetQuotedName(string name)
{
return string.Format("\"{0}\"", name);
}
private string GetQuotedTrueValue()
{
return GetQuotedValue(true, typeof(bool));
}
private string GetQuotedFalseValue()
{
return GetQuotedValue(false, typeof(bool));
}
public virtual string GetQuotedValue(object value, Type fieldType)
{
return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue);
}
protected virtual string GetFieldName(Database.PocoData pocoData, string name)
{
var column = pocoData.Columns.FirstOrDefault(x => x.Value.PropertyInfo.Name == name);
@@ -472,34 +53,10 @@ namespace Umbraco.Core.Persistence.Querying
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(pocoData.TableInfo.TableName),
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(column.Value.ColumnName));
}
private string GetTrueExpression()
{
object o = GetQuotedTrueValue();
return string.Format("({0}={1})", o, o);
}
private string GetFalseExpression()
{
return string.Format("({0}={1})",
GetQuotedTrueValue(),
GetQuotedFalseValue());
}
private bool IsTrueExpression(string exp)
{
return (exp == GetTrueExpression());
}
private bool IsFalseExpression(string exp)
{
return (exp == GetFalseExpression());
}
protected bool IsFieldName(string quotedExp)
{
return true;
}
//protected bool IsFieldName(string quotedExp)
//{
// return true;
//}
}
}
+27 -17
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Umbraco.Core.Persistence.Querying
@@ -10,37 +11,46 @@ namespace Umbraco.Core.Persistence.Querying
/// <typeparam name="T"></typeparam>
public class Query<T> : IQuery<T>
{
//private readonly ExpressionHelper<T> _expresionist = new ExpressionHelper<T>();
private readonly ModelToSqlExpressionHelper<T> _expresionist = new ModelToSqlExpressionHelper<T>();
private readonly List<string> _wheres = new List<string>();
public Query()
: base()
{
}
private readonly List<Tuple<string, object[]>> _wheres = new List<Tuple<string, object[]>>();
/// <summary>
/// Helper method to be used instead of manually creating an instance
/// </summary>
public static IQuery<T> Builder
{
get
{
return new Query<T>();
}
get { return new Query<T>(); }
}
/// <summary>
/// Adds a where clause to the query
/// </summary>
/// <param name="predicate"></param>
/// <returns>This instance so calls to this method are chainable</returns>
public virtual IQuery<T> Where(Expression<Func<T, bool>> predicate)
{
if (predicate != null)
{
string whereExpression = _expresionist.Visit(predicate);
_wheres.Add(whereExpression);
var expressionHelper = new ModelToSqlExpressionHelper<T>();
string whereExpression = expressionHelper.Visit(predicate);
_wheres.Add(new Tuple<string, object[]>(whereExpression, expressionHelper.GetSqlParameters()));
}
return this;
}
public List<string> WhereClauses()
/// <summary>
/// Returns all translated where clauses and their sql parameters
/// </summary>
/// <returns></returns>
public IEnumerable<Tuple<string, object[]>> GetWhereClauses()
{
return _wheres;
}
[Obsolete("This is no longer used, use the GetWhereClauses method which includes the SQL parameters")]
public List<string> WhereClauses()
{
return _wheres.Select(x => x.Item1).ToList();
}
}
}
@@ -15,14 +15,10 @@ namespace Umbraco.Core.Persistence.Querying
if (sql == null)
throw new Exception("Sql cannot be null");
var query1 = query as Query<T>;
if (query1 == null)
throw new Exception("Query cannot be null");
_sql = sql;
foreach (var clause in query1.WhereClauses())
foreach (var clause in query.GetWhereClauses())
{
_sql.Where(clause);
_sql.Where(clause.Item1, clause.Item2);
}
}
@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Dynamics;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -255,6 +257,9 @@ namespace Umbraco.Core.Persistence.Repositories
//Ensure unique name on the same level
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
//Ensure that strings don't contain characters that are invalid in XML
entity.SanitizeEntityPropertiesForXmlStorage();
var factory = new ContentFactory(NodeObjectTypeId, entity.Id);
var dto = factory.BuildDto(entity);
@@ -296,9 +301,9 @@ namespace Umbraco.Core.Persistence.Repositories
var userPermissions = (
from perm in parentPermissions
from p in perm.AssignedPermissions
select new Tuple<int, string>(perm.UserId, p)).ToList();
permissionsRepo.ReplaceEntityPermissions(entity, userPermissions);
select new EntityPermissionSet.UserPermission(perm.UserId, p)).ToList();
permissionsRepo.ReplaceEntityPermissions(new EntityPermissionSet(entity.Id, userPermissions));
//flag the entity's permissions changed flag so we can track those changes.
//Currently only used for the cache refreshers to detect if we should refresh all user permissions cache.
((Content) entity).PermissionsChanged = true;
@@ -366,6 +371,9 @@ namespace Umbraco.Core.Persistence.Repositories
//Ensure unique name on the same level
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name, entity.Id);
//Ensure that strings don't contain characters that are invalid in XML
entity.SanitizeEntityPropertiesForXmlStorage();
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
if (((ICanBeDirty)entity).IsPropertyDirty("ParentId"))
{
@@ -402,7 +410,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
//a flag that we'll use later to create the tags in the tag db table
var isNewPublishedVersion = false;
var publishedStateChanged = false;
//If Published state has changed then previous versions should have their publish state reset.
//If state has been changed to unpublished the previous versions publish state should also be reset.
@@ -418,7 +426,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
//this is a newly published version so we'll update the tags table too (end of this method)
isNewPublishedVersion = true;
publishedStateChanged = true;
}
//Look up (newest) entries by id in cmsDocument table to set newest = false
@@ -481,10 +489,15 @@ namespace Umbraco.Core.Persistence.Repositories
}
//lastly, check if we are a newly published version and then update the tags table
if (isNewPublishedVersion)
if (publishedStateChanged && entity.Published)
{
UpdatePropertyTags(entity, _tagRepository);
}
else if (publishedStateChanged && (entity.Trashed || entity.Published == false))
{
//it's in the trash or not published remove all entity tags
ClearEntityTags(entity, _tagRepository);
}
((ICanBeDirty)entity).ResetDirtyProperties();
}
@@ -498,7 +511,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias && property.Value != null &&
string.IsNullOrEmpty(property.Value.ToString()) == false
&& fs.FileExists(IOHelper.MapPath(property.Value.ToString())))
&& fs.FileExists(fs.GetRelativePath(property.Value.ToString())))
{
var relativeFilePath = fs.GetRelativePath(property.Value.ToString());
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
@@ -558,6 +571,12 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
public void ReplaceContentPermissions(EntityPermissionSet permissionSet)
{
var repo = new PermissionRepository<IContent>(UnitOfWork, _cacheHelper);
repo.ReplaceEntityPermissions(permissionSet);
}
public IContent GetByLanguage(int id, string language)
{
var sql = GetBaseQuery(false);
@@ -625,7 +644,117 @@ namespace Umbraco.Core.Persistence.Repositories
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IContent>(previewExists, content, xml));
}
/// <summary>
/// Gets paged content results
/// </summary>
/// <param name="query">Query to excute</param>
/// <param name="pageNumber">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetPagedResultsByQuery(IQuery<IContent> query, int pageNumber, int pageSize, out int totalRecords,
string orderBy, Direction orderDirection, string filter = "")
{
// Get base query
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate()
.Where<DocumentDto>(x => x.Newest);
// Apply filter
if (!string.IsNullOrEmpty(filter))
{
sql = sql.Where("cmsDocument.text LIKE @0", "%" + filter + "%");
}
// Apply order according to parameters
if (!string.IsNullOrEmpty(orderBy))
{
var orderByParams = new[] { GetDatabaseFieldNameForOrderBy(orderBy) };
if (orderDirection == Direction.Ascending)
{
sql = sql.OrderBy(orderByParams);
}
else
{
sql = sql.OrderByDescending(orderByParams);
}
}
// Note we can't do multi-page for several DTOs like we can multi-fetch and are doing in PerformGetByQuery,
// but actually given we are doing a Get on each one (again as in PerformGetByQuery), we only need the node Id.
// So we'll modify the SQL.
var modifiedSQL = sql.SQL.Replace("SELECT *", "SELECT cmsDocument.nodeId");
// Get page of results and total count
IEnumerable<IContent> result;
var pagedResult = Database.Page<DocumentDto>(pageNumber, pageSize, modifiedSQL, sql.Arguments);
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
if (totalRecords > 0)
{
// Parse out node Ids and load content (we need the cast here in order to be able to call the IQueryable extension
// methods OrderBy or OrderByDescending)
var content = GetAll(pagedResult.Items
.DistinctBy(x => x.NodeId)
.Select(x => x.NodeId).ToArray())
.Cast<Content>()
.AsQueryable();
// Now we need to ensure this result is also ordered by the same order by clause
var orderByProperty = GetIContentPropertyNameForOrderBy(orderBy);
if (orderDirection == Direction.Ascending)
{
result = content.OrderBy(orderByProperty);
}
else
{
result = content.OrderByDescending(orderByProperty);
}
}
else
{
result = Enumerable.Empty<IContent>();
}
return result;
}
private string GetDatabaseFieldNameForOrderBy(string orderBy)
{
// Translate the passed order by field (which were originally defined for in-memory object sorting
// of ContentItemBasic instances) to the database field names.
switch (orderBy)
{
case "Name":
return "cmsDocument.text";
case "Owner":
return "umbracoNode.nodeUser";
case "Updator":
return "cmsDocument.documentUser";
default:
return orderBy;
}
}
private string GetIContentPropertyNameForOrderBy(string orderBy)
{
// Translate the passed order by field (which were originally defined for in-memory object sorting
// of ContentItemBasic instances) to the IContent property names.
switch (orderBy)
{
case "Owner":
return "CreatorId";
case "Updator":
return "WriterId";
default:
return orderBy;
}
}
#endregion
/// <summary>
@@ -426,7 +426,7 @@ AND umbracoNode.id <> @id",
{
//go get the data
var dtos = Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = dataTypeId });
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value), x.Alias, x.SortOrder)).ToList();
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList();
var collection = PreValueConverter.ConvertToPreValuesCollection(list);
//now create the cache key, this needs to include all pre-value ids so that we can use this cached item in the GetPreValuesAsString method
@@ -146,6 +146,9 @@ namespace Umbraco.Core.Persistence.Repositories
{
((DictionaryItem)entity).AddingEntity();
foreach (var translation in entity.Translations)
translation.Value = translation.Value.ToValidXmlString();
var factory = new DictionaryItemFactory();
var dto = factory.BuildDto(entity);
@@ -167,6 +170,9 @@ namespace Umbraco.Core.Persistence.Repositories
{
((Entity)entity).UpdatingEntity();
foreach (var translation in entity.Translations)
translation.Value = translation.Value.ToValidXmlString();
var factory = new DictionaryItemFactory();
var dto = factory.BuildDto(entity);
@@ -141,45 +141,70 @@ namespace Umbraco.Core.Persistence.Repositories
{
if (ids.Any())
{
foreach (var id in ids)
return PerformGetAll(objectTypeId, sql1 => sql1.Where(" umbracoNode.id in (@ids)", new {ids = ids}));
}
else
{
return PerformGetAll(objectTypeId);
}
}
public virtual IEnumerable<IUmbracoEntity> GetAll(Guid objectTypeId, params Guid[] keys)
{
if (keys.Any())
{
return PerformGetAll(objectTypeId, sql1 => sql1.Where(" umbracoNode.uniqueID in (@keys)", new { keys = keys }));
}
else
{
return PerformGetAll(objectTypeId);
}
}
private IEnumerable<IUmbracoEntity> PerformGetAll(Guid objectTypeId, Action<Sql> filter = null)
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, filter);
var factory = new UmbracoEntityFactory();
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
foreach (var entity in entities)
{
yield return Get(id, objectTypeId);
yield return entity;
}
}
else
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, string.Empty);
var factory = new UmbracoEntityFactory();
if (isMedia)
var dtos = _work.Database.Fetch<dynamic>(sql);
foreach (var entity in dtos.Select(dto => factory.BuildEntityFromDynamic(dto)))
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
foreach (var entity in entities)
{
yield return entity;
}
}
else
{
var dtos = _work.Database.Fetch<dynamic>(sql);
foreach (var entity in dtos.Select(dto => factory.BuildEntityFromDynamic(dto)))
{
yield return entity;
}
yield return entity;
}
}
}
public virtual IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query)
{
var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query<IUmbracoEntity>) query).WhereClauses()));
var sqlClause = GetBase(false, false, wheres);
//TODO: We need to fix all of this and how it handles parameters!
var wheres = query.GetWhereClauses().ToArray();
var sqlClause = GetBase(false, false, sql1 =>
{
//adds the additional filters
foreach (var whereClause in wheres)
{
sql1.Where(whereClause.Item1, whereClause.Item2);
}
});
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate().Append(GetGroupBy(false, false));
@@ -193,12 +218,21 @@ namespace Umbraco.Core.Persistence.Repositories
public virtual IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId)
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query<IUmbracoEntity>)query).WhereClauses()));
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, wheres, objectTypeId);
var wheres = query.GetWhereClauses().ToArray();
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, sql1 =>
{
//adds the additional filters
foreach (var whereClause in wheres)
{
sql1.Where(whereClause.Item1, whereClause.Item2);
}
}, objectTypeId);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var entitySql = translator.Translate();
@@ -207,7 +241,14 @@ namespace Umbraco.Core.Persistence.Repositories
if (isMedia)
{
var mediaSql = GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), wheres);
var mediaSql = GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), sql =>
{
//adds the additional filters
foreach (var whereClause in wheres)
{
sql.Where(whereClause.Item1, whereClause.Item2);
}
});
//treat media differently for now
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
@@ -246,16 +287,16 @@ namespace Umbraco.Core.Persistence.Repositories
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)));
}
protected Sql GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectTypeId, string additionalWhereClause)
protected Sql GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectTypeId, Action<Sql> filter)
{
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, additionalWhereClause, objectTypeId);
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, filter, objectTypeId);
if (isMedia == false) return entitySql.Append(GetGroupBy(isContent, false));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), filter);
}
private Sql GetFullSqlForMedia(Sql entitySql, string additionWhereStatement = "")
private Sql GetFullSqlForMedia(Sql entitySql, Action<Sql> filter = null)
{
//this will add any dataNvarchar property to the output which can be added to the additional properties
@@ -268,7 +309,12 @@ namespace Umbraco.Core.Persistence.Repositories
.On<PropertyTypeDto, PropertyDataDto>(dto => dto.Id, dto => dto.PropertyTypeId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(dto => dto.DataTypeId, dto => dto.DataTypeId)
.Where("umbracoNode.nodeObjectType = @nodeObjectType" + additionWhereStatement, new {nodeObjectType = Constants.ObjectTypes.Media});
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new {nodeObjectType = Constants.ObjectTypes.Media});
if (filter != null)
{
filter(joinSql);
}
//We're going to create a query to query against the entity SQL
// because we cannot group by nText columns and we have a COUNT in the entitySql we cannot simply left join
@@ -284,7 +330,7 @@ namespace Umbraco.Core.Persistence.Repositories
return wrappedSql;
}
protected virtual Sql GetBase(bool isContent, bool isMedia, string additionWhereStatement = "")
protected virtual Sql GetBase(bool isContent, bool isMedia, Action<Sql> customFilter)
{
var columns = new List<object>
{
@@ -332,43 +378,48 @@ namespace Umbraco.Core.Persistence.Repositories
.On("umbracoNode.id = latest.nodeId");
}
if (customFilter != null)
{
customFilter(entitySql);
}
return entitySql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, string additionWhereStatement, Guid nodeObjectType)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Action<Sql> filter, Guid nodeObjectType)
{
var sql = baseQuery(isContent, isMedia, additionWhereStatement)
var sql = baseQuery(isContent, isMedia, filter)
.Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = nodeObjectType });
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, int id)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, int id)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id", new { Id = id })
.Append(GetGroupBy(isContent, isMedia));
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, Guid key)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid key)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.uniqueID = '" + key + "'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key })
.Append(GetGroupBy(isContent, isMedia));
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, int id)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, int id)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType",
new {Id = id, NodeObjectType = nodeObjectType});
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, Guid key)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, Guid key)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.uniqueID = '" + key + "'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID AND umbracoNode.nodeObjectType = @NodeObjectType",
new { UniqueID = key, NodeObjectType = nodeObjectType });
return sql;
@@ -497,7 +548,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
// Add this UmbracoProperty to the current additional data
Current.AdditionalData[p.PropertyAlias] = new UmbracoEntity.EntityProperty
{
{
PropertyEditorAlias = p.PropertyEditorAlias,
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
@@ -516,7 +567,7 @@ namespace Umbraco.Core.Persistence.Repositories
var prev = Current;
// Setup the new current UmbracoEntity
Current = _factory.BuildEntityFromDynamic(a);
if (p != null && p.PropertyAlias.IsNullOrWhiteSpace() == false)
@@ -1,14 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentRepository : IRepositoryVersionable<int, IContent>
{
/// <summary>
/// Used to bulk update the permissions set for a content item. This will replace all permissions
/// assigned to an entity with a list of user id & permission pairs.
/// </summary>
/// <param name="permissionSet"></param>
void ReplaceContentPermissions(EntityPermissionSet permissionSet);
/// <summary>
/// Gets a specific language version of an <see cref="IContent"/>
/// </summary>
@@ -59,5 +68,18 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="xml"></param>
void AddOrUpdatePreviewXml(IContent content, Func<IContent, XElement> xml);
/// <summary>
/// Gets paged content results
/// </summary>
/// <param name="query">Query to excute</param>
/// <param name="pageNumber">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IContent> GetPagedResultsByQuery(IQuery<IContent> query, int pageNumber, int pageSize, out int totalRecords,
string orderBy, Direction orderDirection, string filter = "");
}
}
@@ -12,6 +12,17 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IRepositoryVersionable<TId, TEntity> : IRepositoryQueryable<TId, TEntity>
where TEntity : IAggregateRoot
{
/// <summary>
/// Get the total count of entities
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
int Count(string contentTypeAlias = null);
int CountChildren(int parentId, string contentTypeAlias = null);
int CountDescendants(int parentId, string contentTypeAlias = null);
/// <summary>
/// Gets a list of all versions for an <see cref="TEntity"/>.
/// </summary>
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Returns all tags for an entity type (content/media/member)
/// </summary>
/// <param name="objectType"></param>
/// <param name="objectType">Entity type</param>
/// <param name="group">Optional group</param>
/// <returns></returns>
IEnumerable<ITag> GetTagsForEntityType(TaggableObjectTypes objectType, string group = null);
@@ -57,5 +57,18 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="propertyTypeId"></param>
/// <param name="tags">The tags to remove from the property</param>
void RemoveTagsFromProperty(int contentId, int propertyTypeId, IEnumerable<ITag> tags);
/// <summary>
/// Removes all tag associations from the property
/// </summary>
/// <param name="contentId"></param>
/// <param name="propertyTypeId"></param>
void ClearTagsFromProperty(int contentId, int propertyTypeId);
/// <summary>
/// Removes all tag associations from the entity
/// </summary>
/// <param name="contentId"></param>
void ClearTagsFromEntity(int contentId);
}
}
@@ -188,10 +188,22 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
//only update if it's dirty
if (macro.Properties[propDto.Alias].IsDirty())
//This will only work if the Alias hasn't been changed
if (macro.Properties.ContainsKey(propDto.Alias))
{
Database.Update(propDto);
//only update if it's dirty
if (macro.Properties[propDto.Alias].IsDirty())
{
Database.Update(propDto);
}
}
else
{
var property = macro.Properties.FirstOrDefault(x => x.Id == propDto.Id);
if (property != null && property.IsDirty())
{
Database.Update(propDto);
}
}
}
}
@@ -220,6 +220,9 @@ namespace Umbraco.Core.Persistence.Repositories
//Ensure unique name on the same level
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
//Ensure that strings don't contain characters that are invalid in XML
entity.SanitizeEntityPropertiesForXmlStorage();
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
var dto = factory.BuildDto(entity);
@@ -289,6 +292,9 @@ namespace Umbraco.Core.Persistence.Repositories
//Ensure unique name on the same level
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name, entity.Id);
//Ensure that strings don't contain characters that are invalid in XML
entity.SanitizeEntityPropertiesForXmlStorage();
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
if (((ICanBeDirty)entity).IsPropertyDirty("ParentId"))
{
@@ -369,7 +375,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias &&
string.IsNullOrEmpty(property.Value.ToString()) == false
&& fs.FileExists(IOHelper.MapPath(property.Value.ToString())))
&& fs.FileExists(fs.GetRelativePath(property.Value.ToString())))
{
var relativeFilePath = fs.GetRelativePath(property.Value.ToString());
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
@@ -203,13 +203,13 @@ namespace Umbraco.Core.Persistence.Repositories
//find the member by username
var memberSql = new Sql();
var memberObjectType = new Guid(Constants.ObjectTypes.Member);
var escapedUsername = PetaPocoExtensions.EscapeAtSymbols(username);
memberSql.Select("umbracoNode.id")
.From<NodeDto>()
.InnerJoin<MemberDto>()
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
.Where<MemberDto>(x => x.LoginName == escapedUsername);
.Where<MemberDto>(x => x.LoginName == username);
var memberIdUsername = Database.Fetch<int?>(memberSql).FirstOrDefault();
if (memberIdUsername.HasValue == false)
{
@@ -280,6 +280,9 @@ namespace Umbraco.Core.Persistence.Repositories
public void AssignRolesInternal(int[] memberIds, string[] roleNames)
{
//ensure they're unique
memberIds = memberIds.Distinct().ToArray();
//create the missing roles first
var existingSql = new Sql()
@@ -209,6 +209,9 @@ namespace Umbraco.Core.Persistence.Repositories
{
((Member)entity).AddingEntity();
//Ensure that strings don't contain characters that are invalid in XML
entity.SanitizeEntityPropertiesForXmlStorage();
var factory = new MemberFactory(NodeObjectTypeId, entity.Id);
var dto = factory.BuildDto(entity);
@@ -284,6 +287,9 @@ namespace Umbraco.Core.Persistence.Repositories
//Updates Modified date
((Member)entity).UpdatingEntity();
//Ensure that strings don't contain characters that are invalid in XML
entity.SanitizeEntityPropertiesForXmlStorage();
var dirtyEntity = (ICanBeDirty) entity;
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
@@ -398,7 +404,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
if (property.PropertyType.PropertyEditorAlias == uploadFieldAlias &&
string.IsNullOrEmpty(property.Value.ToString()) == false
&& fs.FileExists(IOHelper.MapPath(property.Value.ToString())))
&& fs.FileExists(fs.GetRelativePath(property.Value.ToString())))
{
var relativeFilePath = fs.GetRelativePath(property.Value.ToString());
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
@@ -523,10 +529,10 @@ namespace Umbraco.Core.Persistence.Repositories
public bool Exists(string username)
{
var sql = new Sql();
var escapedUserName = PetaPocoExtensions.EscapeAtSymbols(username);
sql.Select("COUNT(*)")
.From<MemberDto>()
.Where<MemberDto>(x => x.LoginName == escapedUserName);
.Where<MemberDto>(x => x.LoginName == username);
return Database.ExecuteScalar<int>(sql) > 0;
}
@@ -191,25 +191,23 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Assigns permissions to an entity for multiple users/permission entries
/// </summary>
/// <param name="entity"></param>
/// <param name="userPermissions">
/// A key/value pair list containing a userId and a permission to assign
/// <param name="permissionSet">
/// </param>
/// <remarks>
/// This will first clear the permissions for this entity then re-create them
/// </remarks>
public void ReplaceEntityPermissions(TEntity entity, IEnumerable<Tuple<int, string>> userPermissions)
public void ReplaceEntityPermissions(EntityPermissionSet permissionSet)
{
var db = _unitOfWork.Database;
using (var trans = db.GetTransaction())
{
db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId", new { nodeId = entity.Id });
db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId", new { nodeId = permissionSet.EntityId });
var actions = userPermissions.Select(p => new User2NodePermissionDto
var actions = permissionSet.UserPermissionsSet.Select(p => new User2NodePermissionDto
{
NodeId = entity.Id,
Permission = p.Item2,
UserId = p.Item1
NodeId = permissionSet.EntityId,
Permission = p.Permission,
UserId = p.UserId
}).ToArray();
_unitOfWork.Database.BulkInsertRecords(actions, trans);
@@ -133,6 +133,9 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns></returns>
public IEnumerable<TEntity> GetAll(params TId[] ids)
{
//ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries
ids = ids.Distinct().ToArray();
if (ids.Any())
{
var entities = _cache.GetByIds(
@@ -64,6 +64,9 @@ namespace Umbraco.Core.Persistence.Repositories
public override IEnumerable<Script> GetAll(params string[] ids)
{
//ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries
ids = ids.Distinct().ToArray();
if (ids.Any())
{
foreach (var id in ids)
@@ -106,6 +106,9 @@ namespace Umbraco.Core.Persistence.Repositories
public override IEnumerable<Stylesheet> GetAll(params string[] ids)
{
//ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries
ids = ids.Distinct().ToArray();
if (ids.Any())
{
foreach (var id in ids)
@@ -160,6 +160,8 @@ namespace Umbraco.Core.Persistence.Repositories
//TODO: Consider caching implications.
//TODO: We need to add lookups for parentId or path! (i.e. get content in tag group that are descendants of x)
public IEnumerable<TaggedEntity> GetTaggedEntitiesByTagGroup(TaggableObjectTypes objectType, string tagGroup)
{
@@ -230,63 +232,96 @@ namespace Umbraco.Core.Persistence.Repositories
{
var nodeObjectType = GetNodeObjectType(objectType);
var sql = new Sql()
.Select("DISTINCT cmsTags.*")
.From<TagDto>()
.InnerJoin<TagRelationshipDto>()
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id)
var sql = GetTagsQuerySelect(true);
sql = ApplyRelationshipJoinToTagsQuery(sql);
sql = sql
.InnerJoin<ContentDto>()
.On<ContentDto, TagRelationshipDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == nodeObjectType);
if (group.IsNullOrWhiteSpace() == false)
{
sql = sql.Where<TagDto>(dto => dto.Group == group);
}
sql = ApplyGroupFilterToTagsQuery(sql, group);
var factory = new TagFactory();
sql = ApplyGroupByToTagsQuery(sql);
return Database.Fetch<TagDto>(sql).Select(factory.BuildEntity);
return ExecuteTagsQuery(sql);
}
public IEnumerable<ITag> GetTagsForEntity(int contentId, string group = null)
{
var sql = new Sql()
.Select("DISTINCT cmsTags.*")
.From<TagDto>()
.InnerJoin<TagRelationshipDto>()
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id)
var sql = GetTagsQuerySelect();
sql = ApplyRelationshipJoinToTagsQuery(sql);
sql = sql
.Where<TagRelationshipDto>(dto => dto.NodeId == contentId);
if (group.IsNullOrWhiteSpace() == false)
{
sql = sql.Where<TagDto>(dto => dto.Group == group);
}
sql = ApplyGroupFilterToTagsQuery(sql, group);
var factory = new TagFactory();
return Database.Fetch<TagDto>(sql).Select(factory.BuildEntity);
return ExecuteTagsQuery(sql);
}
public IEnumerable<ITag> GetTagsForProperty(int contentId, string propertyTypeAlias, string group = null)
{
var sql = new Sql()
.Select("DISTINCT cmsTags.*")
.From<TagDto>()
.InnerJoin<TagRelationshipDto>()
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id)
var sql = GetTagsQuerySelect();
sql = ApplyRelationshipJoinToTagsQuery(sql);
sql = sql
.InnerJoin<PropertyTypeDto>()
.On<PropertyTypeDto, TagRelationshipDto>(left => left.Id, right => right.PropertyTypeId)
.Where<TagRelationshipDto>(dto => dto.NodeId == contentId)
.Where<PropertyTypeDto>(dto => dto.Alias == propertyTypeAlias);
if (group.IsNullOrWhiteSpace() == false)
sql = ApplyGroupFilterToTagsQuery(sql, group);
return ExecuteTagsQuery(sql);
}
private Sql GetTagsQuerySelect(bool withGrouping = false)
{
var sql = new Sql();
if (withGrouping)
{
sql = sql.Select("cmsTags.Id, cmsTags.Tag, cmsTags." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group") + @", Count(*) NodeCount");
}
else
{
sql = sql.Select("DISTINCT cmsTags.*");
}
return sql;
}
private Sql ApplyRelationshipJoinToTagsQuery(Sql sql)
{
return sql
.From<TagDto>()
.InnerJoin<TagRelationshipDto>()
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id);
}
private Sql ApplyGroupFilterToTagsQuery(Sql sql, string group)
{
if (!group.IsNullOrWhiteSpace())
{
sql = sql.Where<TagDto>(dto => dto.Group == group);
}
return sql;
}
private Sql ApplyGroupByToTagsQuery(Sql sql)
{
return sql.GroupBy(new string[] { "cmsTags.Id", "cmsTags.Tag", "cmsTags." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group") + @"" });
}
private IEnumerable<ITag> ExecuteTagsQuery(Sql sql)
{
var factory = new TagFactory();
return Database.Fetch<TagDto>(sql).Select(factory.BuildEntity);
@@ -405,11 +440,23 @@ namespace Umbraco.Core.Persistence.Repositories
" AND tagId IN ",
"(SELECT id FROM cmsTags INNER JOIN ",
tagSetSql,
" ON (TagSet.Tag = cmsTags.Tag and TagSet.[Group] = cmsTags.[Group]))");
" ON (TagSet.Tag = cmsTags.Tag and TagSet." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group") + @" = cmsTags." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group") + @"))");
Database.Execute(deleteSql);
}
public void ClearTagsFromProperty(int contentId, int propertyTypeId)
{
Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId = @nodeId AND propertyTypeId = @propertyTypeId",
new { nodeId = contentId, propertyTypeId = propertyTypeId });
}
public void ClearTagsFromEntity(int contentId)
{
Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId = @nodeId",
new { nodeId = contentId });
}
private Guid GetNodeObjectType(TaggableObjectTypes type)
{
switch (type)
@@ -439,9 +486,13 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns></returns>
private static string GetTagSet(IEnumerable<ITag> tagsToInsert)
{
//TODO: Fix this query, since this is going to be basically a unique query each time, this will cause some mem usage in peta poco,
// and surely there's a nicer way!
//TODO: When we fix, be sure to remove the @ symbol escape
var array = tagsToInsert
.Select(tag =>
string.Format("select '{0}' as Tag, '{1}' as [Group]",
string.Format("select '{0}' as Tag, '{1}' as " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group") + @"",
PetaPocoExtensions.EscapeAtSymbols(tag.Text.Replace("'", "''")), tag.Group))
.ToArray();
return "(" + string.Join(" union ", array).Replace(" ", " ") + ") as TagSet";
@@ -261,10 +261,10 @@ namespace Umbraco.Core.Persistence.Repositories
public bool Exists(string username)
{
var sql = new Sql();
var escapedUserName = PetaPocoExtensions.EscapeAtSymbols(username);
sql.Select("COUNT(*)")
.From<UserDto>()
.Where<UserDto>(x => x.UserName == escapedUserName);
.Where<UserDto>(x => x.UserName == username);
return Database.ExecuteScalar<int>(sql) > 0;
}
@@ -2,11 +2,14 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -93,6 +96,100 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
public int CountDescendants(int parentId, string contentTypeAlias = null)
{
var pathMatch = parentId == -1
? "-1,"
: "," + parentId + ",";
var sql = new Sql();
if (contentTypeAlias.IsNullOrWhiteSpace())
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.Path.Contains(pathMatch));
}
else
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.InnerJoin<ContentDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.Path.Contains(pathMatch))
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
}
return Database.ExecuteScalar<int>(sql);
}
public int CountChildren(int parentId, string contentTypeAlias = null)
{
var sql = new Sql();
if (contentTypeAlias.IsNullOrWhiteSpace())
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.ParentId == parentId);
}
else
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.InnerJoin<ContentDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.ParentId == parentId)
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
}
return Database.ExecuteScalar<int>(sql);
}
/// <summary>
/// Get the total count of entities
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
public int Count(string contentTypeAlias = null)
{
var sql = new Sql();
if (contentTypeAlias.IsNullOrWhiteSpace())
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
}
else
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.InnerJoin<ContentDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
}
return Database.ExecuteScalar<int>(sql);
}
/// <summary>
/// This removes associated tags from the entity - used generally when an entity is recycled
/// </summary>
/// <param name="entity"></param>
/// <param name="tagRepo"></param>
protected void ClearEntityTags(IContentBase entity, ITagsRepository tagRepo)
{
tagRepo.ClearTagsFromEntity(entity.Id);
}
/// <summary>
/// Updates the tag repository with any tag enabled properties and their values
/// </summary>
@@ -133,16 +230,37 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="updateDate"></param>
/// <returns></returns>
protected PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentTypeComposition contentType, DateTime createDate, DateTime updateDate)
{
var sql = new Sql();
sql.Select("*")
sql.Select("cmsPropertyData.*, cmsDataTypePreValues.id as preValId, cmsDataTypePreValues.value, cmsDataTypePreValues.sortorder, cmsDataTypePreValues.alias, cmsDataTypePreValues.datatypeNodeId")
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.LeftOuterJoin<DataTypePreValueDto>()
.On<PropertyTypeDto, DataTypePreValueDto>(left => left.DataTypeId, right => right.DataTypeNodeId)
.Where<PropertyDataDto>(x => x.NodeId == id)
.Where<PropertyDataDto>(x => x.VersionId == versionId);
var propertyDataDtos = Database.Fetch<PropertyDataDto, PropertyTypeDto>(sql);
var allData = Database.Fetch<dynamic>(sql);
var propertyDataDtos = allData.Select(x => new PropertyDataDto
{
Date = x.dataDate,
Id = x.id,
Integer = x.dataInt,
NodeId = x.contentNodeId,
Text = x.dataNtext,
VarChar = x.dataNvarchar,
VersionId = x.versionId,
PropertyTypeId = x.propertytypeid,
//NOTE: This get's used for nothing so we don't need to map it
//PropertyTypeDto = new PropertyTypeDto()
}).Distinct();
var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate);
var properties = propertyFactory.BuildEntity(propertyDataDtos).ToArray();
@@ -156,6 +274,39 @@ namespace Umbraco.Core.Persistence.Repositories
property.Id = primaryKey;
}
foreach (var property in properties)
{
var editor = PropertyEditorResolver.Current.GetByAlias(property.PropertyType.PropertyEditorAlias);
//TODO: Should this be cached somehow? Might need to benchmark this
var tagSupport = TagExtractor.GetAttribute(editor);
if (tagSupport != null)
{
//this property has tags, so we need to extract them and for that we need the prevals which we've already looked up
var preValData = allData.Where(x => x.propertytypeid == property.PropertyTypeId && x.preValId != null)
.Select(x => new DataTypePreValueDto
{
Alias = x.alias,
DataTypeNodeId = x.datatypeNodeId,
Id = x.preValId,
SortOrder = x.sortorder,
Value = x.value
})
.ToDictionary(x => x.Alias, x => new PreValue(x.Id, x.Value, x.SortOrder));
var preVals = new PreValueCollection(preValData);
var d = new ContentPropertyData(property.Value,
preVals,
new Dictionary<string, object>());
TagExtractor.SetPropertyTags(property, d, property.Value, tagSupport);
}
}
return new PropertyCollection(properties);
}
}
@@ -13,10 +13,19 @@ namespace Umbraco.Core.Persistence.SqlSyntax
{
string EscapeString(string val);
string GetWildcardPlaceholder();
string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType);
string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType);
[Obsolete("Use the overload with the parameter index instead")]
string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType);
[Obsolete("Use the overload with the parameter index instead")]
string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType);
[Obsolete("Use the overload with the parameter index instead")]
string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType);
[Obsolete("Use the overload with the parameter index instead")]
string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType);
[Obsolete("Use the overload with the parameter index instead")]
string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType);
string GetQuotedTableName(string tableName);
@@ -0,0 +1,120 @@
using System;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Abstract class for defining MS sql implementations
/// </summary>
/// <typeparam name="TSyntax"></typeparam>
public abstract class MicrosoftSqlSyntaxProviderBase<TSyntax> : SqlSyntaxProviderBase<TSyntax>
where TSyntax : ISqlSyntaxProvider
{
public override string RenameTable { get { return "sp_rename '{0}', '{1}'"; } }
public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } }
public override string GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);
}
public override string GetQuotedColumnName(string columnName)
{
return string.Format("[{0}]", columnName);
}
public override string GetQuotedName(string name)
{
return string.Format("[{0}]", name);
}
public override string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEqualComparison(column, paramIndex, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for = comparison with NText columns but allows this syntax
return string.Format("{0} LIKE @{1}", column, paramIndex);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnWildcardComparison(column, paramIndex, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE @{1}", column, paramIndex);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
[Obsolete("Use the overload with the parameter index instead")]
public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnStartsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
[Obsolete("Use the overload with the parameter index instead")]
public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEndsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
[Obsolete("Use the overload with the parameter index instead")]
public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
[Obsolete("Use the overload with the parameter index instead")]
public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
}
}
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// Represents an SqlSyntaxProvider for Sql Ce
/// </summary>
[SqlSyntaxProviderAttribute("System.Data.SqlServerCe.4.0")]
public class SqlCeSyntaxProvider : SqlSyntaxProviderBase<SqlCeSyntaxProvider>
public class SqlCeSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlCeSyntaxProvider>
{
public SqlCeSyntaxProvider()
{
@@ -60,6 +60,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return indexType;
}
[Obsolete("Use the overload with the parameter index instead")]
public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
@@ -74,76 +75,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
}
}
public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnStartsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEndsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);
}
public override string GetQuotedColumnName(string columnName)
{
return string.Format("[{0}]", columnName);
}
public override string GetQuotedName(string name)
{
return string.Format("[{0}]", name);
}
public override string FormatColumnRename(string tableName, string oldName, string newName)
{
@@ -285,10 +217,10 @@ ORDER BY TABLE_NAME, INDEX_NAME");
}
}
public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } }
public override string DropIndex { get { return "DROP INDEX {1}.{0}"; } }
public override string RenameTable { get { return "sp_rename '{0}', '{1}'"; } }
}
}
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
@@ -10,7 +9,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// Represents an SqlSyntaxProvider for Sql Server
/// </summary>
[SqlSyntaxProviderAttribute("System.Data.SqlClient")]
public class SqlServerSyntaxProvider : SqlSyntaxProviderBase<SqlServerSyntaxProvider>
public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlServerSyntaxProvider>
{
public SqlServerSyntaxProvider()
{
@@ -33,91 +32,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// Gets/sets the version of the current SQL server instance
/// </summary>
internal Lazy<SqlServerVersionName> VersionName { get; set; }
public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEqualComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for = comparison with NText columns but allows this syntax
return string.Format("{0} LIKE '{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnStartsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnEndsWithComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '%{1}%'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType)
{
switch (columnType)
{
case TextColumnType.NVarchar:
return base.GetStringColumnContainsComparison(column, value, columnType);
case TextColumnType.NText:
//MSSQL doesn't allow for upper methods with NText columns
return string.Format("{0} LIKE '{1}'", column, value);
default:
throw new ArgumentOutOfRangeException("columnType");
}
}
public override string GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);
}
public override string GetQuotedColumnName(string columnName)
{
return string.Format("[{0}]", columnName);
}
public override string GetQuotedName(string name)
{
return string.Format("[{0}]", name);
}
public override IEnumerable<string> GetTablesInSchema(Database db)
{
@@ -218,12 +134,11 @@ order by T.name, I.name");
get { return "ALTER TABLE [{0}] DROP CONSTRAINT [DF_{0}_{1}]"; }
}
public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } }
public override string DropIndex { get { return "DROP INDEX {0} ON {1}"; } }
public override string RenameColumn { get { return "sp_rename '{0}.{1}', '{2}', 'COLUMN'"; } }
public override string RenameTable { get { return "sp_rename '{0}', '{1}'"; } }
}
}
@@ -33,6 +33,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
};
}
public string GetWildcardPlaceholder()
{
return "%";
}
public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})";
public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})";
@@ -108,34 +113,51 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return PetaPocoExtensions.EscapeAtSymbols(val.Replace("'", "''"));
}
public virtual string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return string.Format("upper({0}) = upper(@{1})", column, paramIndex);
}
public virtual string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return string.Format("upper({0}) LIKE upper(@{1})", column, paramIndex);
}
[Obsolete("Use the overload with the parameter index instead")]
public virtual string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return string.Format("upper({0}) = '{1}'", column, value.ToUpper());
}
[Obsolete("Use the overload with the parameter index instead")]
public virtual string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return string.Format("upper({0}) like '{1}%'", column, value.ToUpper());
return string.Format("upper({0}) LIKE '{1}%'", column, value.ToUpper());
}
[Obsolete("Use the overload with the parameter index instead")]
public virtual string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return string.Format("upper({0}) like '%{1}'", column, value.ToUpper());
return string.Format("upper({0}) LIKE '%{1}'", column, value.ToUpper());
}
[Obsolete("Use the overload with the parameter index instead")]
public virtual string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return string.Format("upper({0}) like '%{1}%'", column, value.ToUpper());
return string.Format("upper({0}) LIKE '%{1}%'", column, value.ToUpper());
}
[Obsolete("Use the overload with the parameter index instead")]
public virtual string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return string.Format("upper({0}) like '{1}'", column, value.ToUpper());
return string.Format("upper({0}) LIKE '{1}'", column, value.ToUpper());
}
public virtual string GetQuotedTableName(string tableName)
@@ -22,12 +22,12 @@
/// <remarks>
/// See: http://issues.umbraco.org/issue/U4-3876
/// </remarks>
public static string GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery)
public static Sql GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery)
{
return string.Format(@"DELETE FROM {0} WHERE {1} IN (SELECT {1} FROM ({2}) x)",
sqlProvider.GetQuotedTableName(tableName),
sqlProvider.GetQuotedColumnName(columnName),
subQuery.SQL);
return new Sql(string.Format(@"DELETE FROM {0} WHERE {1} IN (SELECT {1} FROM ({2}) x)",
sqlProvider.GetQuotedTableName(tableName),
sqlProvider.GetQuotedColumnName(columnName),
subQuery.SQL), subQuery.Arguments);
}
}
}

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