Compare commits

...

268 Commits

Author SHA1 Message Date
Niels Lyngsø 8c264b0815 correcting k to K 2019-05-03 09:54:09 +02:00
Peter Duncanson 12ecc5d650 Index types added in this commit (see: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types) 2019-04-29 00:40:28 +01:00
Peter Duncanson 26dce2c99d Strengthened the types up in the appstate.service with some interfaces and index types as we use a funky accessor method in this one
See: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types for details on index types.
2019-04-29 00:39:41 +01:00
Peter Duncanson 4649811abd Initial conversion of appstate.service to typescript 2019-04-28 23:38:21 +01:00
Peter Duncanson 6af5795a1d Renamed appstate.service to .ts extension 2019-04-28 22:31:17 +01:00
Peter Duncanson 68f6e27d20 Refactored the namespaces for Overlay.service 2019-04-26 18:28:10 +01:00
Peter Duncanson ef035df8b8 Updated formHelper to use the new Notification namespaces, also binned off iHasScope 2019-04-26 18:27:38 +01:00
Peter Duncanson 35e6a64e73 Changed namespace to be umbrac.services.Notifications and removed the old alias too 2019-04-26 18:22:27 +01:00
Peter Duncanson 5bb6ffc654 Added some missed types to Overlay and changed to umbraco.services.Overlays namespace 2019-04-26 18:19:30 +01:00
Peter Duncanson 30905a9442 Converted search.service to TypeScript (after changing extension in the last commit) 2019-04-26 18:17:44 +01:00
Peter Duncanson ccfec9e092 Converted search.service to TypeScript 2019-04-26 18:17:13 +01:00
Peter Duncanson a2007a96af Converted overlayhelper.,service to TypeScript 2019-04-26 02:23:52 +01:00
Peter Duncanson 0879bdb90e Renamed overlayhelper.service.js to .ts 2019-04-26 02:19:25 +01:00
Peter Duncanson 1f483bc95c Converted overlay.service to TypeScript 2019-04-26 02:18:54 +01:00
Peter Duncanson e1df9d7a00 Renamed overlay.service.js to .ts 2019-04-26 01:50:30 +01:00
Peter Duncanson 41718cb7d5 package-lock.json had changed 2019-04-26 01:27:07 +01:00
Peter Duncanson 75d6e02c63 Changed iHasScope to "any" type instead of "object" as we don't know what shape it might be 2019-04-26 01:25:59 +01:00
Peter Duncanson c1d09c2516 Move the enum's for notification types out of the models namespace on notification service 2019-04-26 01:25:19 +01:00
Peter Duncanson ed429786ca Switched from using object to any for a few types I didn't know, lesson learnt 2019-04-26 01:24:18 +01:00
Peter Duncanson 88f108bf2e Finished converting over formHelper.service.ts 2019-04-26 00:50:14 +01:00
Peter Duncanson 437bf6bea1 Added new iHasScope interface to angularHelper
Used when anyone is passing around an object with a scope field on it
2019-04-26 00:49:52 +01:00
Peter Duncanson 6eb678b821 Converted all the classes to begin with Uppercase as per JS common practise, added a lowercase alias though so we don't break anything 2019-04-25 23:56:16 +01:00
Peter Duncanson 4d3af9284f "Prettified" notifications.service.ts no logic changes just formatting 2019-04-25 23:48:58 +01:00
Peter Duncanson 38d531894f Renamed formhelper to .ts and started converting to TypeScript 2019-04-25 23:42:34 +01:00
Stephan c434558378 IsVisible moves from publishde content to element 2019-04-10 12:44:21 +01:00
Stephan 777276d2e9 Better mapping of enumerable 2019-04-10 12:44:20 +01:00
Stephan b63ff54c19 Fix mapping of enumerable 2019-04-10 12:44:20 +01:00
Niels Lyngsø 96f1189fc6 Fixing model sync, much weird code cleaned up 2019-04-10 12:44:20 +01:00
Niels Lyngsø 2250389cc2 Fixing checkbox list to work in v8 2019-04-10 12:44:20 +01:00
Kenn Jacobsen 6a2b6ba75b Add IsVisible extension on IPublishedElement 2019-04-10 12:44:20 +01:00
Craig Noble 0a43530852 #5215 - Set up the initial build for typescript
I have rewritten the notificationsService and the AngularHelper (used by the NotificationsService) in Typescript. This gives a very good example of how the notificationsService can reference a concrete type even though the angularHelper is being injected in the constructor.

Open the Umbraco.Web.UI.Client in an editor that supports Typescript, such as VS Code or Atom.

In Umbraco.Web.UI.Client, run npm install.  Then run "npm run dev" and it will do the following:
1. Compile the typescript into Umbraco.Web.UI.Client/src/common/services/build/temp/, into their respective files such as notifications.service.js
2. The existing JS build picks up the compiled JS files and merges them into umbraco.services.js, then is saved in Umbraco.Web.UI/Umbraco/js (all stuff it currently does)

I converted the controller into a class, within the umbraco.services namespace.  The benefit of this is having the ability to generate a definitions file that will retain all namespaces and therefore make it extremely easy to find what you are looking for when creating Umbraco extensions such as custom property types, dashboards etc.

Note: I have set up a definitions folder for global variables that are declared outside of Typescript.  We can add typings in for Angular etc at a later date if needed.
2019-04-10 11:58:19 +01:00
Shannon Deminick 980528c652 Merge pull request #5197 from redmorello/v8/dev
Fix spelling mistake in the Packages section of the back office
2019-04-09 10:33:10 +10:00
Shannon Deminick 0f6f7294a2 Merge pull request #5193 from kjac/v8-feature-donotcloneattribute-public
V8: Make DoNotCloneAttribute public
2019-04-09 10:25:52 +10:00
Ian Houghton a90ae4ae28 Fix spelling mistake 2019-04-08 14:48:42 +01:00
Stephan c4db0a3dc1 Merge pull request #5188 from kjac/v8-fix-relationtype-map-alias
V8: Relation type alias is "generating" when editing an existing relation type
2019-04-08 14:32:24 +02:00
Kenn Jacobsen ceaae13575 Make DoNotCloneAttribute public 2019-04-08 10:39:37 +02:00
Stephan 091fae39a3 Merge pull request #5177 from kjac/v8-fix-entityslim-additionaldata-mapping
V8: Missing "metadata" breaks media picker (mapping issue)
2019-04-08 08:59:07 +02:00
Kenn Jacobsen 22f43e67f5 Include relation type alias when mapping to RelationTypeDisplay 2019-04-07 20:13:39 +02:00
Stephan b0ef69a4ec Log more details about boot environment 2019-04-07 16:24:35 +02:00
Stephan 5cb85ebd52 Merge branch 'v8/bugfix/5163-mapping-confusion' into v8/dev 2019-04-07 13:00:14 +02:00
Stephan 89b39be83f Comment out dangerous methods 2019-04-07 12:59:59 +02:00
Stephan ce89aa4857 Minor mapping fix 2019-04-07 12:56:11 +02:00
Stephan 6aeea47f6f Minor fix 2019-04-07 11:27:34 +02:00
Stephan 8049c40f90 Stop exposing mapper in MapperContext 2019-04-07 11:26:47 +02:00
Stephan 1af431a27d Merge pull request #5181 from kjac/v8-fix-variant-listview
V8: Fix YSOD on variant content listview
2019-04-07 10:34:01 +02:00
Stephan 814b8e3b7d Merge pull request #5180 from kjac/v8-fix-save-variant-content
V8: YSOD when saving variant content (mapping issue)
2019-04-07 10:29:37 +02:00
Kenn Jacobsen 9326aac6d3 Pass the mapping context when mapping properties, to ensure that variant properties are mapped according to the current language context 2019-04-06 21:53:40 +02:00
Kenn Jacobsen cb921b1fe2 Pass the mapping context when mapping properties, otherwise variant properties can't be mapped due to missing culture in mapping context 2019-04-06 21:39:16 +02:00
Stephan 9536a868e0 Fix erroneous log message when upgrading 2019-04-06 19:11:01 +02:00
Stephan 0a4465f721 Fix mapping comment 2019-04-06 18:35:07 +02:00
Stephan 9c06ef83e2 Merge pull request #5176 from kjac/v8-fix-map-usergroup-assignedusergrouppermissions
V8: Fix node permissions YSOD (mapping issue)
2019-04-06 18:33:04 +02:00
Kenn Jacobsen f5ad4c67eb Include AdditionalData when mapping IEntitySlim to EntityBasic 2019-04-06 12:24:41 +02:00
Kenn Jacobsen 851f74528f Include UserGroup ID when mapping UserGroup to AssignedUserGroupPermissions 2019-04-06 08:18:40 +02:00
Stephan 4f6f5cc3fd Merge pull request #5173 from kjac/v8-fix-create-content-type-with-template
V8: YSOD when creating the same content type twice (mapping issue)
2019-04-05 19:05:23 +02:00
Kenn Jacobsen a9ceb82158 Don't explode when attempting to create a new content type with a template (mapping issue) 2019-04-05 15:18:34 +02:00
Bjarke Berg 9d9c201984 Merge pull request #5140 from umbraco/temp8-ui-use-umb-checkbox-for-select-all-in-listview
Use umb-checkbox for select all in listview
2019-04-05 11:33:10 +02:00
Bjarke Berg c21ffd4417 Merge pull request #5166 from umbraco/v8/bugfix/ui-umb-toggle-v8-look
Changed umb-toggle look to fit with v8
2019-04-05 10:38:44 +02:00
Niels Lyngsø afad606d05 Changed umb-toggle look to fit with v8 2019-04-05 09:14:19 +02:00
Stephan 406fd892b4 Document UmbracoMapper 2019-04-05 09:08:00 +02:00
Stephan 4ceef88f59 Merge pull request #5165 from kjac/v8-fix-template-mapping
V8: Can't save templates (missing mapping profile)
2019-04-05 09:06:44 +02:00
Bjarke Berg bdacb7da76 Merge pull request #5011 from umbraco/v8/feature/284-content-type-repo
Content/Media/MemberTypeRepository refactoring
2019-04-05 08:21:11 +02:00
Bjarke Berg 36c35750cd Merge remote-tracking branch 'origin/v8/dev' into v8/feature/284-content-type-repo 2019-04-05 07:49:55 +02:00
Shannon Deminick a4c3fdd634 Merge pull request #5162 from umbraco/v8/bugfix/5121-nucache-missing-media
NuCache: fix loading the media cache from local files, was missing media
2019-04-05 10:48:26 +11:00
Shannon 84d69b363a Merge remote-tracking branch 'origin/v8/dev' into v8/bugfix/5121-nucache-missing-media 2019-04-05 10:46:44 +11:00
Shannon Deminick c6038c6920 Merge pull request #5114 from umbraco/v8/bugfix/4998-nucache-large-volumes
Enable configuration of NuCache BTree block size
2019-04-05 10:43:02 +11:00
Shannon da8fea205a Merge branch 'v8/dev' into v8/bugfix/4998-nucache-large-volumes 2019-04-05 10:39:32 +11:00
Kenn Jacobsen 1999d6121a Proper fix for the mapping 2019-04-04 20:09:21 +02:00
Kenn Jacobsen 295f805f6a Can't save template (fix template mapping) 2019-04-04 19:57:10 +02:00
Stephan 4ec51da737 NuCache: fix loading the media cache from local files, was missing media 2019-04-04 17:57:04 +02:00
Shannon Deminick d0b9fb7d6f Merge pull request #5087 from umbraco/v8/feature/284-auto-mapper
Refactor (auto) mapping
2019-04-05 00:59:22 +11:00
Shannon bff920fa50 Merge branch 'v8/dev' into v8/feature/284-auto-mapper
# Conflicts:
#	src/Umbraco.Core/Composing/SetCollectionBuilderBase.cs
2019-04-05 00:58:56 +11:00
Shannon ac6020ff79 removes the unneeded ctor param 2019-04-05 00:56:18 +11:00
Shannon 2e1dd7f4a9 splits appstate.service and editorstate.service 2019-04-05 00:11:05 +11:00
Shannon fe2c519d75 fixes tests 2019-04-05 00:00:22 +11:00
Shannon Deminick 984c31f586 Merge pull request #4955 from umbraco/temp8-server-content-validation-fixes-4884
v8 - Fixes multiple issues with variant validation
2019-04-04 22:15:26 +11:00
Shannon 9da236809a Merge branch 'temp8-server-content-validation-fixes-4884' of https://github.com/umbraco/Umbraco-CMS into temp8-server-content-validation-fixes-4884 2019-04-04 21:58:11 +11:00
Shannon 92b64b2ca6 Changes controller to be explicit about which culture will be affiliated with invariant property issues 2019-04-04 21:57:49 +11:00
Kenn Jacobsen 2c6d400055 Various UX improvements to the RTE configuration (#5122) 2019-04-04 12:44:19 +02:00
Kenn Jacobsen 4a8449d80d Replace listview bulk action checkboxes with umb-toggle (#5128) 2019-04-04 11:47:03 +02:00
Stephan dc337a60ac NuCache dashboard cosmetics 2019-04-04 11:08:33 +02:00
Shannon Deminick 3505984b4d Merge pull request #5138 from Jeavon/bugfix/xmlhelper-public
Make XmlHelper class public again
2019-04-04 19:51:13 +11:00
Jeavon e23b9153a0 fix: set some methods within XmlHelper to internal 2019-04-04 09:45:04 +01:00
Stephan 571df1894a Cleanup fixmes 2019-04-04 08:36:16 +02:00
Stephan f66e92801d Merge branch 'v8/dev' into temp8-server-content-validation-fixes-4884 2019-04-04 08:27:00 +02:00
Stephan 90c5ddf0dc BaseMapper does not need be IDiscoverable 2019-04-04 08:26:26 +02:00
Bjarke Berg fd6368946c Merge pull request #5146 from umbraco/v8/bugfix/4811
use text-overflow for better look in narrow screens
2019-04-04 07:54:25 +02:00
Shannon Deminick 7d248bd2c8 Merge pull request #5106 from umbraco/v8/feature/284-npoco-mappers
Simplify our Poco mappers
2019-04-04 15:39:05 +11:00
Niels Lyngsø 8472b48ba8 use text-overflow for better look in narrow screens 2019-04-03 15:45:41 +02:00
Matt Darby 7568ab20ac Fix users layout jumping when a user is selected (#4939)
* Remove "xs" sizing from selected user buttons
* Only remove "xs" from first button
2019-04-03 15:31:09 +02:00
Bjarke Berg 74bfaabf90 Added null-check before updating the count. (#5142) 2019-04-03 13:35:01 +02:00
Bjarke Berg a0f5a466ac Merge pull request #5141 from umbraco/temp8-4427-3
check if value exists
2019-04-03 13:22:04 +02:00
Niels Lyngsø 2529193ca9 finished the work 2019-04-03 13:07:00 +02:00
Niels Lyngsø 92a68d3887 check if value exists 2019-04-03 12:46:55 +02:00
Stephan e6b26d72e6 NuCache dashboard cosmetics 2019-04-03 11:57:45 +02:00
Jeavon ef79e6c0e9 fix: make XmlHelper class public 2019-04-03 10:54:53 +01:00
Bjarke Berg a99c8ad2ba UI-Fix for readonly values on members. 2019-04-03 11:40:51 +02:00
Niels Lyngsø 9379efceae early testing 2019-04-03 11:22:00 +02:00
Stephan aad245d5a8 Fix mappers 2019-04-03 11:17:51 +02:00
Stephan 7fa71a2f4e Fix mappers 2019-04-03 11:03:35 +02:00
Stephan 7a6884d208 Rename mapper stuff 2019-04-03 10:39:49 +02:00
Stephan eb2a506dd8 Merge pull request #5135 from andrewdeans/v8/dev
Fix case of query parameter
2019-04-03 08:00:17 +02:00
Shannon Deminick a6fa23426e Merge pull request #5125 from kjac/v8-fix-members-disable-bulk-move
V8: Don't allow move in members listview
2019-04-03 16:21:10 +11:00
Andrew Deans 6ac5e19e9f Fix case of query parameter 2019-04-02 23:00:08 -04:00
Shannon Deminick b73d798b54 Merge pull request #5117 from kjac/v8-fix-listview-reload-after-move
V8: Reload listview after moving items
2019-04-03 10:44:00 +11:00
Shannon Deminick f5d1ae0a3f Merge pull request #5130 from MMasey/update-syncing-fork-info
fix: update contributing.md with correct branch to rebase with (v8/dev)
2019-04-03 10:35:04 +11:00
Mike Masey 6d6121faa6 feat: add updated defaultbranch image and update missed dev-v8 references to v8/dev 2019-04-02 20:47:47 +01:00
Mike Masey 9d4ecc1d41 fix: update contributing.md with correct branch to rebase with (v8/dev) 2019-04-02 20:09:03 +01:00
Stephan 389ee7ec84 Review, cleanup 2019-04-02 18:02:44 +02:00
Kenn Jacobsen 9fe86169dc Don't allow move in members listview 2019-04-02 15:43:57 +02:00
Stephan fd8cef59ef Merge branch 'v8/dev' into temp8-server-content-validation-fixes-4884 2019-04-02 14:03:02 +02:00
Bjarke Berg a45eda9d19 #284 - Removed unused variables/parameters 2019-04-02 13:46:37 +02:00
Stephan 27e145fe3d Fix temporal coupling 2019-04-02 13:35:01 +02:00
Stephan 04427e4c61 Merge branch 'v8/dev' into v8/feature/284-npoco-mappers 2019-04-02 12:03:55 +02:00
Stephan 4e7e784d7c Merge branch 'v8/dev' into v8/feature/284-content-type-repo 2019-04-02 12:03:26 +02:00
Stephan c5f934930e Merge branch 'v8/dev' into v8/feature/284-auto-mapper 2019-04-02 12:03:05 +02:00
Stephan b524019fc4 Merge branch 'v8/dev' into v8/bugfix/4998-nucache-large-volumes 2019-04-02 12:02:45 +02:00
Bjarke Berg 06bee124ee Merge pull request #4560 from umbraco/temp8-4011
Temp8 Fix for #4011
2019-04-02 09:47:30 +02:00
Bjarke Berg a9f5d3c1e8 #4011 - Use discreet word not disgrete 2019-04-02 09:41:29 +02:00
Stephan bf1e9b48be Comment (bump PR) 2019-04-02 09:40:14 +02:00
Warren Buckley b0e6618773 Merge pull request #5105 from kjac/v8-fix-logviewer-search-ux
V8: Improvements for logviewer search results view
2019-04-02 09:15:51 +02:00
Bjarke Berg 76e2f2f380 #4011 - Not using a unnecessary function call in views 2019-04-02 09:15:30 +02:00
Bjarke Berg d58106d976 Merge remote-tracking branch 'origin/v8/dev' into temp8-4011 2019-04-02 08:36:39 +02:00
Kenn Jacobsen 392d4f7f96 Reload listview after moving items 2019-04-01 19:56:14 +02:00
Warren Buckley 3af8d0e4f5 Merge pull request #5109 from leekelleher/chore/constants-root
V8: Updated Constant.System.Root references (to use correct constant, and others)
2019-04-01 13:48:33 +01:00
Stephan 92c380fce1 Merge branch v8/dev into temp8-server-content-validation-fixes-4884 2019-04-01 13:48:25 +02:00
Stephan 61cfba506a Merge branch 'temp8-4217-datatype-edits-updates-content-editor' into v8/dev 2019-04-01 12:19:11 +02:00
Stephan 4906231dcd Merge branch 'v8/dev' into temp8-4217-datatype-edits-updates-content-editor 2019-04-01 12:15:57 +02:00
Stephan c1856723ba Merge branch 'temp8-4427-3' into v8/dev 2019-04-01 12:07:00 +02:00
Stephan 58efda69f5 Merge branch 'v8/dev' into temp8-4427-3 2019-04-01 12:00:35 +02:00
Stephan 305bcb3b5d Cleanup 2019-04-01 08:31:33 +02:00
Shannon Deminick b0c7a7bdaf Merge pull request #5079 from umbraco/temp8-4828
fixes 4828 — by adding dimmed-layer for umb-modelcolumn
2019-04-01 17:04:05 +11:00
Shannon Deminick 80d1d73d7c Merge pull request #5107 from umbraco/v8/feature/284-no-viewbag
Replace dynamic ViewBag with ViewData
2019-04-01 16:47:13 +11:00
Shannon Deminick 6a8ae22c6c Merge pull request #5013 from umbraco/v8/feature/5012-messenger-options
Better ServerMessenger configuration
2019-04-01 16:29:21 +11:00
Shannon b4885f48d7 fixing bad merge problem 2019-04-01 14:31:34 +11:00
Shannon d0d5d37400 Merge remote-tracking branch 'origin/v8/dev' into temp8-4011
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js
#	src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html
2019-04-01 14:27:11 +11:00
Shannon 043f4f3d02 Revert "Merge remote-tracking branch 'origin/v8/8.0' into temp8-4011"
This reverts commit e73cf590b7, reversing
changes made to 89903a633b.
2019-04-01 14:20:47 +11:00
Shannon e73cf590b7 Merge remote-tracking branch 'origin/v8/8.0' into temp8-4011 2019-04-01 14:16:13 +11:00
Stephan 1eb55a83c0 Merge branch 'patch-8.0.1' into v8/8.0 2019-03-30 12:13:12 +01:00
Stephan 2b2db8bf24 Enable configuration of NuCache BTree block size 2019-03-29 19:18:22 +01:00
leekelleher 0a687ab7ce Whitespace formatting in the Constants classes
Also removed unused namespaces.
2019-03-29 12:26:42 +00:00
leekelleher 533d2cf8dd Replaced hardcoded "-1" with Constants.System.RootString 2019-03-29 12:18:52 +00:00
leekelleher b44360aa99 Swapped System.RecycleBinMedia string conversions
to use the `System.RecycleBinMediaString` constant.

This saves on integer parsing and reduces the number of
new strings being created.

Since the `System.RecycleBinMedia` is `-21`, it didn't
need the culture invariant conversion.
2019-03-29 12:17:12 +00:00
Kenn Jacobsen 31e4701487 Re-style things so the saved searches link is inside the input field 2019-03-29 13:17:00 +01:00
leekelleher a4c96678e5 Swapped System.RecycleBinContent string conversions
to use the `System.RecycleBinContentString` constant.

This saves on integer parsing and reduces the number of
new strings being created.

Since the `System.RecycleBinContentString` is `-20`, it didn't
need the culture invariant conversion.
2019-03-29 12:14:27 +00:00
leekelleher 13564c72ef Replaced hardcoded "-1" with Constants.System.RootString 2019-03-29 12:13:38 +00:00
leekelleher f4bd0cd1f5 Swapped all System.Root string conversions
to use the `System.RootString` constant.

This saves on integer parsing and reduces the number of
new strings being created.

Since the `System.Root` is `-1`, it didn't need the culture invariant
conversion.
2019-03-29 12:06:23 +00:00
Stephan cea8753ff2 Replace dynamic ViewBag with ViewData 2019-03-29 09:56:56 +01:00
Stephan 961cbf694e Simplify our Poco mappers 2019-03-29 09:46:36 +01:00
Warren Buckley 853087a750 Merge pull request #4970 from poornimanayar/cleanup-logview-saved-searches
General tidy up of styles for logviewer
2019-03-29 08:23:17 +00:00
Bjarke Berg 0d1683fa61 Merge pull request #5091 from umbraco/temp8-4669-ui-discreet-option-for-set-date-input
UI changes for Schedule Publishing
2019-03-29 08:37:52 +01:00
Stephan 07bb7ac0f7 Add content.SetValue overload for posted files 2019-03-29 08:26:36 +01:00
Shannon Deminick 212af8be10 Merge pull request #4877 from kjac/v8-feature-get-multiple-members
V8: Add methods for fetching multiple members to UmbracoHelper
2019-03-29 11:19:51 +11:00
Kenn Jacobsen 6a69906fb6 Make the "save search" dialog a little prettier 2019-03-28 17:11:00 +01:00
Kenn Jacobsen d49d4bcdeb Align the logviewer search icons to the right of the search field 2019-03-28 17:10:41 +01:00
Shannon 28210896a8 fixes logic with publish due to flag enums 2019-03-29 00:32:52 +11:00
Shannon c67298dcd4 recommitting, the build server just died for some reason 2019-03-29 00:08:53 +11:00
Shannon 5a626acb3a Fixes up a bunch more validation logic, adds unit tests to support 2019-03-28 23:59:49 +11:00
Kenn Jacobsen 6d9ed6c59b Change Member to Members (plural) for methods returning multiple members 2019-03-28 12:46:54 +01:00
Stephan f8f1fea407 Various corrections to mappers 2019-03-28 12:38:04 +01:00
Niels Lyngsø 8da9ecc9e4 date-wrapper styles for variants schedule publishing 2019-03-27 12:49:03 +01:00
Stephan 9b13d8f5d7 Stop logging useless errors in UriExtensions 2019-03-27 10:29:11 +01:00
Stephan c9f1d9aaf6 Merge branch 'temp8-4839-siblings' into dev-v8 2019-03-27 10:21:14 +01:00
Stephan 8f51e2634c Merge branch 'dev-v8' into temp8-4839-siblings 2019-03-27 10:17:57 +01:00
Stephan 47cb03bb38 Fix mapper and mapping 2019-03-27 09:35:57 +01:00
Stephan 72bdf56ddf Cleanup and fix mappers 2019-03-27 09:15:32 +01:00
Stephan 70c2090a56 Refactor more mappers - AutoMapper gone now 2019-03-27 09:15:32 +01:00
Stephan d274737296 Refactor more mappers 2019-03-27 09:15:32 +01:00
Stephan 92139dd0c9 Refactor more mappers 2019-03-27 09:15:32 +01:00
Stephan 5bcafba34c Refactor more mappers 2019-03-27 09:15:32 +01:00
Stephan a6dc5f0eac Refactor more mappers 2019-03-27 09:15:31 +01:00
Stephan cd0338498f Fix BackOfficeIdentityUser 2019-03-27 09:15:31 +01:00
Stephan 57067d7e56 Use Umbraco.Code to ensure proper mapping 2019-03-27 09:15:31 +01:00
Stephan ef5646fa3b Cleanup, reduce AutoMapper exposure 2019-03-27 09:15:31 +01:00
Stephan b43e113cb6 Experiment with mapping (perfs) 2019-03-27 09:15:31 +01:00
Bjarke Berg 13c11e0434 Merge pull request #5065 from umbraco/temp8-ui-change-notification-style
V8: UI changed notification style
2019-03-27 09:07:21 +01:00
Bjarke Berg 97879fc7aa Merge pull request #5063 from umbraco/temp8-ui-change-redirectmanagement-button-styles
V8: changed button styles for a better low hanging-fruit solution
2019-03-27 08:41:01 +01:00
Niels Lyngsø 719a4a8b6a adjusted size 2019-03-27 08:29:04 +01:00
Shannon ef3f349764 Fixes issue with ContentService.SaveAndPublish when dealing with cultures since it wouldn't actually save the content if it was invalid - we always must save even if data is invalid, this was an oversight with the rush to release with these last minute APIs 2019-03-27 18:00:17 +11:00
Shannon 15a984f68c Merge branch 'dev-v8' into temp8-server-content-validation-fixes-4884
# Conflicts:
#	src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
2019-03-27 16:33:26 +11:00
Shannon 22662042b5 Fixes up more of dealing with invariant property validation 2019-03-27 16:31:53 +11:00
Shannon 8042405d94 WIP - Fixing up validation so that invariant properties are only validated on the default lang or if the item is not published 2019-03-27 12:41:02 +11:00
Shannon e222bf2920 Reverts the previous changes of validating all languages against invariant properties 2019-03-27 12:39:05 +11:00
Kenn Jacobsen 4d0d17eb1b Remove params overloads on MembershipHelper 2019-03-26 15:11:05 +01:00
Niels Lyngsø d6be02aaec style set date input 2019-03-26 14:57:45 +01:00
Niels Lyngsø 4a14d45a64 remove inline styling 2019-03-26 14:57:30 +01:00
Niels Lyngsø 458e510961 remove double ;; 2019-03-26 14:57:13 +01:00
Niels Lyngsø b36288b987 infinity editing dim-layer for umb-modelcolumn 2019-03-26 14:11:01 +01:00
Stephan dfe6f2029a NuCache: better fixing, cleanup 2019-03-26 10:25:05 +01:00
Stephan 851c844c8b NuCache: fix panic exception 2019-03-26 10:25:05 +01:00
Stephan 7e413ed406 NuCache: troubleshooting 2019-03-26 10:25:05 +01:00
Stephan c56b8863ee NuCache: reorg some classes 2019-03-26 10:25:05 +01:00
Stephan 13a8548d3a NuCache: rename things 2019-03-26 10:25:05 +01:00
Stephan a70cb51559 NuCache: cleanup scoped objects 2019-03-26 10:25:05 +01:00
Kenn Jacobsen 8a56b3db16 Fix the "Language" dropdown in the "Culture and Hostnames" dialog 2019-03-26 20:10:50 +11:00
Bjarke Berg 4fcf2ce7bd Fixed issue when creating variant content templates, but the properties is invariant. Before this change an exception was thrown 2019-03-26 20:10:03 +11:00
Shannon 080ace90b2 Bugfix: https://github.com/umbraco/Umbraco-CMS/issues/4827 - Fixed MediaPickerValueConverter when config is Only Images 2019-03-26 20:09:12 +11:00
Shannon 2bfa81c707 fixes 4627 + makes front-end validations appear as soon as they are invalid again. 2019-03-26 20:07:42 +11:00
Shannon Deminick ac6767248e Merge pull request #4978 from umbraco/temp8-4627
Fixes front-end validation (4627)
2019-03-26 01:54:06 -07:00
Kenn Jacobsen 64bae439bf Fix stuff from review 2019-03-26 08:44:03 +01:00
Niels Lyngsø 118f6c7e50 added the ability to watch tokens in localization service 2019-03-25 14:46:42 +01:00
Niels Lyngsø 50ee60e8c3 correcting translate tokens to use % 2019-03-25 14:09:29 +01:00
Niels Lyngsø 286978647e fix type mistake, plus correcting math. 2019-03-25 14:04:31 +01:00
Niels Lyngsø 0cc6e51b7a simplify controller + only show text when near limit + different text when exceeding limit 2019-03-25 13:46:57 +01:00
Niels Lyngsø 15d5c67cd3 global text-color variables 2019-03-25 13:46:49 +01:00
Niels Lyngsø 84e4189b45 added margin and round corners to notifications 2019-03-25 11:37:05 +01:00
Niels Lyngsø e6720a175c changed notification auto remove time to 10 seconds 2019-03-25 11:36:50 +01:00
Robert 026ad4f2a8 fixes - TinyMCE does not sync data to angular store when dragged away in grid-editor (#5048) 2019-03-25 11:07:50 +01:00
Niels Lyngsø bf3ce014cb changed button styles for a better low hanging-fruit solution 2019-03-25 10:51:06 +01:00
Sebastiaan Janssen 929fc415bb Merge branch 'dev-v7' into dev-v8
# Conflicts:
#	src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs
2019-03-23 10:31:50 +01:00
Nathan Woulfe 4e1634754b fix URL redirection creation - was failing when save and save+publish as save creates new URL, which is compared to itself in save+publish (#5036) 2019-03-23 10:29:46 +01:00
Brandon Osborne 080f433140 Updated Upload method (#4850) 2019-03-23 09:59:04 +01:00
Niels Lyngsø 054bc28a0a Merge remote-tracking branch 'origin/dev-v8' into temp8-4217-datatype-edits-updates-content-editor 2019-03-22 16:35:53 +01:00
Niels Lyngsø d806855698 listen for "editors.documentType.saved" instead of "editors.content.reload" 2019-03-22 16:22:36 +01:00
Niels Lyngsø da503cc9b5 listen for "editors.dataTypeSettings.saved" 2019-03-22 16:19:56 +01:00
Niels Lyngsø af4dc97b10 changed ' to " 2019-03-22 16:06:39 +01:00
Niels Lyngsø 019ac52309 emit "editors.dataTypeSettings.saved"-event 2019-03-22 16:06:07 +01:00
Robert 3761d2e483 Merge branch 'dev-v8' of https://github.com/umbraco/Umbraco-CMS into dev-v8 2019-03-22 15:06:44 +01:00
Robert d107289a08 Fixes not being able to add languages 2019-03-22 15:06:09 +01:00
Robert 1d8ed6e507 Merge pull request #5045 from umbraco/v8/bugfix/4683-Change-text-in-culture-and-hostnames
V8: Translation in the culture and hostnames panel.
2019-03-22 14:37:21 +01:00
Robert 4ea169a636 Merge pull request #5049 from umbraco/v8/bugfix/4990-use-skip-and-take-parameters
V8: Use the skip and take parameters in GetVersionsSlim
2019-03-22 14:32:51 +01:00
Bjarke Berg b16bf78c44 Merge pull request #4957 from kjac/v8-fix-region-and-regionless-languages
V8: Allow mixing languages with and without region
2019-03-22 14:03:10 +01:00
Bjarke Berg acf59dc1d0 Formatting 2019-03-22 13:55:46 +01:00
Bjarke Berg f99d3f7419 https://github.com/umbraco/Umbraco-CMS/issues/4990 - Added skip and take 2019-03-22 13:53:40 +01:00
Bjarke Berg 8ab89f5e1c https://github.com/umbraco/Umbraco-CMS/issues/4683 - Translation in the culture and hostnames panel. Our recommendation in V8 is opposite to V7, therefore a new key is introduced 2019-03-22 12:01:08 +01:00
Bjarke Berg 1587e13b23 Merge pull request #5043 from umbraco/temp8-4695-umb-sticky-bar-position-infinity-mode
top: 70px; in infinity mode
2019-03-22 11:42:00 +01:00
Niels Lyngsø 23f36b9d4e top: 70px; in infinity mode 2019-03-22 09:20:50 +01:00
Robert ad17677951 Fix tests 2019-03-20 10:20:00 +01:00
Stephan 1879215e7c Sliding Cache 2019-03-19 16:29:11 +01:00
Stephan 4e87dd389d Refactor Content/Media/Member type repositories 2019-03-19 16:28:50 +01:00
Robert e9ecb4c998 Only show server-side validation errors if the languages are checked for publish/schedule 2019-03-19 15:03:07 +01:00
Bjarke Berg e4c6ef05a9 Introduced both Siblings and SiblingsAndSelf 2019-03-19 14:59:52 +01:00
Robert aa6e0ce0c2 Show server validation errors only if the language is checked for publish. 2019-03-19 14:50:28 +01:00
Robert 268658812a Return all languages if a variant has a invalid invariant property 2019-03-19 14:43:21 +01:00
Bjarke Berg e73029cf4f Rename siblings to ParentChildren (Because the actual returned list contains the content node self) 2019-03-19 13:08:08 +01:00
Stephan 7f33454002 Better ServerMessenger configuration 2019-03-18 17:51:04 +01:00
Shannon 19ad595f5d Adds back in the umb-list-item--error css class when any row in a content publish dialog is invalid 2019-03-15 15:47:46 +11:00
Shannon c8de8b1ad5 Merge remote-tracking branch 'origin/dev-v8' into temp8-server-content-validation-fixes-4884 2019-03-15 15:23:21 +11:00
Stephan e72133f322 Merge branch 'dev-v8' into temp8-4839-siblings 2019-03-14 17:15:43 +01:00
Shannon 1290244602 slight change of wording 2019-03-14 14:19:40 +11:00
Shannon 9d8b5be650 Fixes mandatory validation issue when creating a new document and a mandatory language has validation problems 2019-03-14 14:18:42 +11:00
Shannon 4eedc3ce5c oops, put back logic i removed during testing 2019-03-14 11:46:49 +11:00
Shannon b8c609ddc9 removes old notes, makes MediaSaveModelValidator to be consistent 2019-03-14 11:41:29 +11:00
Shannon 88f2a410f3 adds notes to html about variant notifications, removes the styling from the publish dialog since it's not needed 2019-03-14 11:33:20 +11:00
Shannon 38cf7a4e49 Fixes content service to not validate every culture for properties, only the cultures passed in to to the method, removes client side name check, 2019-03-14 01:55:39 +11:00
Shannon 8c3388507d Merge remote-tracking branch 'origin/dev-v8' into temp8-server-content-validation-fixes-4884 2019-03-14 01:18:16 +11:00
Shannon d6393616f1 Fixing up server side validation for invariant properties on variant content 2019-03-14 00:59:51 +11:00
Shannon 418dab26a5 Merge remote-tracking branch 'origin/dev-v8' into temp8-server-content-validation-fixes-4884 2019-03-14 00:58:02 +11:00
Niels Lyngsø 53e6c620eb fixes 4627 + makes front-end validations appear as soon as they are invalid again. 2019-03-13 14:45:30 +01:00
Shannon 3d6db4b812 Ensures media is always validated for critical data, adds notes 2019-03-13 18:12:48 +11:00
Shannon ea93d4dd5d simplifies a js operator 2019-03-13 17:52:17 +11:00
Poornima Nayar 1f7172e98d Cleaned up class names in logviewer overview to use new logviewer classes and made a logviewer specific less stylesheet. Also left aligned the saved searches to make them look better 2019-03-12 21:54:13 +00:00
Kenn Jacobsen 03a5d5b866 Allow mixing languages with and without region 2019-03-12 08:20:43 +01:00
Shannon 5ae6a68078 Fixes how critical data is validated 2019-03-12 16:10:35 +11:00
Shannon 89903a633b Merge branch 'dev-v8' into temp8-4011
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
2019-03-12 11:42:35 +11:00
Kenn Jacobsen 36827cfa9f Move some of the multiget member methods to MembershipHelper 2019-03-08 07:31:39 +01:00
Kenn Jacobsen 7b815cfcb3 Add methods for fetching multiple members to UmbracoHelper 2019-03-05 21:34:49 +01:00
Stephan 02764e0ccc Implement IPublishedContent Siblings 2019-03-05 13:02:59 +01:00
Niels Lyngsø 564117e62e Grid editor to use $watch for values that needs to transform for presentation 2019-02-19 10:19:02 +01:00
Niels Lyngsø 592d8db3e7 removed comment 2019-02-19 10:18:13 +01:00
Niels Lyngsø 6f67318432 corrected color variables 2019-02-19 10:18:01 +01:00
Shannon 9f2280da2f Merge branch 'temp8' into temp8-4011 2019-02-19 12:31:11 +11:00
Bjarke Berg a20fa3d9e2 Merge remote-tracking branch 'origin/temp8' into temp8-4011 2019-02-18 11:13:03 +01:00
Niels Lyngsø 3f13baa8e7 make Embed and Media work for Grid in SplitView when grid is not allowed to vary. 2019-02-18 11:00:47 +01:00
Niels Lyngsø 44b1a738bf clean up css for grid 2019-02-18 10:59:40 +01:00
Niels Lyngsø 2e40911450 get uniq ID by using String.CreateGuid() 2019-02-18 10:59:30 +01:00
Niels Lyngsø 13a326b467 remove logs 2019-02-18 10:58:59 +01:00
Niels Lyngsø 60fa931f3f Grid-RTE got a unique ID, which fixes TineMCE code, which is dependent on a unique ID. #4011 2019-02-13 14:38:25 +01:00
Niels Lyngsø 988be21990 making the grid work in splitview 2019-02-13 13:20:01 +01:00
427 changed files with 16240 additions and 13964 deletions
+187 -187
View File
@@ -1,187 +1,187 @@
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
The following is a set of guidelines for contributing to Umbraco CMS.
These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
Remember, we're a friendly bunch and are happy with whatever contribution you might provide. Below are guidelines for success that we've gathered over the years. If you choose to ignore them then we still love you 💖.
**Code of conduct**
This project and everyone participating in it is governed by the [our Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [Sebastiaan Janssen - sj@umbraco.dk](mailto:sj@umbraco.dk).
**Table of contents**
[Contributing code changes](#contributing-code-changes)
* [Guidelines for contributions we welcome](#guidelines-for-contributions-we-welcome)
* [What can I start with?](#what-can-i-start-with)
* [How do I begin?](#how-do-i-begin)
* [Pull requests](#pull-requests)
[Reviews](#reviews)
* [Styleguides](#styleguides)
* [The PR team](#the-pr-team)
* [Questions?](#questions)
[Working with the code](#working-with-the-code)
* [Building Umbraco from source code](#building-umbraco-from-source-code)
* [Working with the source code](#working-with-the-source-code)
* [Making changes after the PR was opened](#making-changes-after-the-pr-was-opened)
* [Which branch should I target for my contributions?](#which-branch-should-i-target-for-my-contributions)
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
## Contributing code changes
This document gives you a quick overview on how to get started.
### Guidelines for contributions we welcome
Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time.
We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes.
Remember, if an issue is in the `Up for grabs` list or you've asked for some feedback before you sent us a PR, your PR will not be closed as unwanted.
### What can I start with?
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` issues](https://github.com/umbraco/Umbraco-CMS/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs+)
### How do I begin?
Great question! The short version goes like this:
* **Fork** - create a fork of [`Umbraco-CMS` on GitHub](https://github.com/umbraco/Umbraco-CMS)
![Fork the repository](img/forkrepository.png)
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
![Clone the fork](img/clonefork.png)
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md)
* **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `dev-v8`, create a new branch first.
* **Push** - great, now you can push the changes up to your fork on GitHub
* **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
![Create a pull request](img/createpullrequest.png)
### Pull requests
The most successful pull requests usually look a like this:
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
Again, these are guidelines, not strict requirements.
## Reviews
You've sent us your first contribution, congratulations! Now what?
The [pull request team](#the-pr-team) can now start reviewing your proposed changes and give you feedback on them. If it's not perfect, we'll either fix up what we need or we can request you to make some additional changes.
We have [a process in place which you can read all about](REVIEW_PROCESS.md). The very abbreviated version is:
- Your PR will get a reply within 48 hours
- An in-depth reply will be added within at most 2 weeks
- The PR will be either merged or rejected within at most 4 weeks
- Sometimes it is difficult to meet these timelines and we'll talk to you
### Styleguides
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
### The PR team
The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members
- [Anders Bjerner](https://github.com/abjerner)
- [Dave Woestenborghs](https://github.com/dawoe)
- [Emma Burstow](https://github.com/emmaburstow)
- [Poornima Nayar](https://github.com/poornimanayar)
These wonderful volunteers will provide you with a first reply to your PR, review and test out your changes and might ask more questions. After that they'll let Umbraco HQ know if everything seems okay.
### Questions?
You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love open conversations and we are a friendly bunch. No question you have is stupid. Any questions you have usually helps out multiple people with the same question. Ask away:
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely
## Working with the code
### Building Umbraco from source code
In order to build the Umbraco source code locally, first make sure you have the following installed.
* Visual Studio 2017 v15.9.7+
* Node v10+
* npm v6.4.1+
The easiest way to get started is to run `build.ps1` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (version 15.9.7 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile.
![Gulp build in Visual Studio](img/gulpbuild.png)
After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser, follow the directions there to get a working Umbraco install up and running.
### Working with the source code
Some parts of our source code are over 10 years old now. And when we say "old", we mean "mature" of course!
There's two big areas that you should know about:
1. The Umbraco backoffice is a extensible AngularJS app and requires you to run a `gulp dev` command while you're working with it, so changes are copied over to the appropriate directories and you can refresh your browser to view the results of your changes.
You may need to run the following commands to set up gulp properly:
```
npm cache clean --force
npm install
npm run build
```
2. "The rest" is a C# based codebase, which is mostly ASP.NET MVC based. You can make changes, build them in Visual Studio, and hit `F5` to see the result.
To find the general areas of something you're looking to fix or improve, have a look at the following two parts of the API documentation.
* [The AngularJS based backoffice files](https://our.umbraco.com/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The C# application](https://our.umbraco.com/apidocs/csharp/)
### Which branch should I target for my contributions?
We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `dev-v8`. Whatever the default is, that's where we'd like you to target your contributions.
![Which branch should I target?](img/defaultbranch.png)
### Making changes after the PR was opened
If you make the corrections we ask for in the same branch and push them to your fork again, the pull request automatically updates with the additional commit(s) so we can review it again. If all is well, we'll merge the code and your commits are forever part of Umbraco!
### Keeping your Umbraco fork in sync with the main repository
We recommend you sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier.
Also, if you've submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course.
To sync your fork with this original one, you'll have to add the upstream url, you only have to do this once:
```
git remote add upstream https://github.com/umbraco/Umbraco-CMS.git
```
Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/dev-v8
```
In this command we're syncing with the `dev-v8` branch, but you can of course choose another one if needed.
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
The following is a set of guidelines for contributing to Umbraco CMS.
These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
Remember, we're a friendly bunch and are happy with whatever contribution you might provide. Below are guidelines for success that we've gathered over the years. If you choose to ignore them then we still love you 💖.
**Code of conduct**
This project and everyone participating in it is governed by the [our Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [Sebastiaan Janssen - sj@umbraco.dk](mailto:sj@umbraco.dk).
**Table of contents**
[Contributing code changes](#contributing-code-changes)
* [Guidelines for contributions we welcome](#guidelines-for-contributions-we-welcome)
* [What can I start with?](#what-can-i-start-with)
* [How do I begin?](#how-do-i-begin)
* [Pull requests](#pull-requests)
[Reviews](#reviews)
* [Styleguides](#styleguides)
* [The PR team](#the-pr-team)
* [Questions?](#questions)
[Working with the code](#working-with-the-code)
* [Building Umbraco from source code](#building-umbraco-from-source-code)
* [Working with the source code](#working-with-the-source-code)
* [Making changes after the PR was opened](#making-changes-after-the-pr-was-opened)
* [Which branch should I target for my contributions?](#which-branch-should-i-target-for-my-contributions)
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
## Contributing code changes
This document gives you a quick overview on how to get started.
### Guidelines for contributions we welcome
Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time.
We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes.
Remember, if an issue is in the `Up for grabs` list or you've asked for some feedback before you sent us a PR, your PR will not be closed as unwanted.
### What can I start with?
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` issues](https://github.com/umbraco/Umbraco-CMS/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs+)
### How do I begin?
Great question! The short version goes like this:
* **Fork** - create a fork of [`Umbraco-CMS` on GitHub](https://github.com/umbraco/Umbraco-CMS)
![Fork the repository](img/forkrepository.png)
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
![Clone the fork](img/clonefork.png)
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md)
* **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/dev`, create a new branch first.
* **Push** - great, now you can push the changes up to your fork on GitHub
* **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
![Create a pull request](img/createpullrequest.png)
### Pull requests
The most successful pull requests usually look a like this:
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
Again, these are guidelines, not strict requirements.
## Reviews
You've sent us your first contribution, congratulations! Now what?
The [pull request team](#the-pr-team) can now start reviewing your proposed changes and give you feedback on them. If it's not perfect, we'll either fix up what we need or we can request you to make some additional changes.
We have [a process in place which you can read all about](REVIEW_PROCESS.md). The very abbreviated version is:
- Your PR will get a reply within 48 hours
- An in-depth reply will be added within at most 2 weeks
- The PR will be either merged or rejected within at most 4 weeks
- Sometimes it is difficult to meet these timelines and we'll talk to you
### Styleguides
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
### The PR team
The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members
- [Anders Bjerner](https://github.com/abjerner)
- [Dave Woestenborghs](https://github.com/dawoe)
- [Emma Burstow](https://github.com/emmaburstow)
- [Poornima Nayar](https://github.com/poornimanayar)
These wonderful volunteers will provide you with a first reply to your PR, review and test out your changes and might ask more questions. After that they'll let Umbraco HQ know if everything seems okay.
### Questions?
You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love open conversations and we are a friendly bunch. No question you have is stupid. Any questions you have usually helps out multiple people with the same question. Ask away:
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely
## Working with the code
### Building Umbraco from source code
In order to build the Umbraco source code locally, first make sure you have the following installed.
* Visual Studio 2017 v15.9.7+
* Node v10+
* npm v6.4.1+
The easiest way to get started is to run `build.ps1` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (version 15.9.7 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile.
![Gulp build in Visual Studio](img/gulpbuild.png)
After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser, follow the directions there to get a working Umbraco install up and running.
### Working with the source code
Some parts of our source code are over 10 years old now. And when we say "old", we mean "mature" of course!
There's two big areas that you should know about:
1. The Umbraco backoffice is a extensible AngularJS app and requires you to run a `gulp dev` command while you're working with it, so changes are copied over to the appropriate directories and you can refresh your browser to view the results of your changes.
You may need to run the following commands to set up gulp properly:
```
npm cache clean --force
npm install
npm run build
```
2. "The rest" is a C# based codebase, which is mostly ASP.NET MVC based. You can make changes, build them in Visual Studio, and hit `F5` to see the result.
To find the general areas of something you're looking to fix or improve, have a look at the following two parts of the API documentation.
* [The AngularJS based backoffice files](https://our.umbraco.com/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The C# application](https://our.umbraco.com/apidocs/csharp/)
### Which branch should I target for my contributions?
We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `v8/dev`. Whatever the default is, that's where we'd like you to target your contributions.
![Which branch should I target?](img/defaultbranch.png)
### Making changes after the PR was opened
If you make the corrections we ask for in the same branch and push them to your fork again, the pull request automatically updates with the additional commit(s) so we can review it again. If all is well, we'll merge the code and your commits are forever part of Umbraco!
### Keeping your Umbraco fork in sync with the main repository
We recommend you sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier.
Also, if you've submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course.
To sync your fork with this original one, you'll have to add the upstream url, you only have to do this once:
```
git remote add upstream https://github.com/umbraco/Umbraco-CMS.git
```
Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/v8/dev
```
In this command we're syncing with the `v8/dev` branch, but you can of course choose another one if needed.
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
+40 -40
View File
@@ -1,40 +1,40 @@
# [Umbraco CMS](https://umbraco.com) · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=dev-v8)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
Umbraco is the friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 500,000 websites worldwide. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
Learn more at [umbraco.com](https://umbraco.com)
<p align="center">
<img src="img/logo.png" alt="Umbraco Logo" />
</p>
See the official [Umbraco website](https://umbraco.com) for an introduction, core mission and values of the product and team behind it.
- [Getting Started](#getting-started)
- [Documentation](#documentation)
- [Community](#join-the-umbraco-community)
- [Contributing](#contributing)
Please also see our [Code of Conduct](CODE_OF_CONDUCT.md).
## Getting Started
[Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet, with full support for all your custom .NET code and integrations. You're up and running in less than a minute, and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14-day trial, no credit card needed.
If you want to DIY, you can [download Umbraco]((https://our.umbraco.com/download)) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host it yourself, and handling deployments and upgrades will be all up to you.
## Documentation
The documentation for Umbraco CMS can be found [on Our Umbraco](https://our.umbraco.com/documentation/). The source for the Umbraco docs is [open source as well](https://github.com/umbraco/UmbracoDocs) and we're happy to look at your documentation contributions.
## Join the Umbraco community
Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com/). Our Umbraco features forums for questions and answers, documentation, downloadable plugins for Umbraco, and a rich collection of community resources.
Besides "Our", we all support each other also via Twitter: [Umbraco HQ](https://twitter.com/umbraco), [Release Updates](https://twitter.com/umbracoproject), [#umbraco](https://twitter.com/hashtag/umbraco)
## Contributing
Umbraco is contribution-focused and community-driven. If you want to contribute back to the Umbraco source code, please check out our [guide to contributing](CONTRIBUTING.md).
# [Umbraco CMS](https://umbraco.com) &middot; [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=v8/dev)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
Umbraco is the friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 500,000 websites worldwide. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
Learn more at [umbraco.com](https://umbraco.com)
<p align="center">
<img src="img/logo.png" alt="Umbraco Logo" />
</p>
See the official [Umbraco website](https://umbraco.com) for an introduction, core mission and values of the product and team behind it.
- [Getting Started](#getting-started)
- [Documentation](#documentation)
- [Community](#join-the-umbraco-community)
- [Contributing](#contributing)
Please also see our [Code of Conduct](CODE_OF_CONDUCT.md).
## Getting Started
[Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet, with full support for all your custom .NET code and integrations. You're up and running in less than a minute, and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14-day trial, no credit card needed.
If you want to DIY, you can [download Umbraco]((https://our.umbraco.com/download)) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host it yourself, and handling deployments and upgrades will be all up to you.
## Documentation
The documentation for Umbraco CMS can be found [on Our Umbraco](https://our.umbraco.com/documentation/). The source for the Umbraco docs is [open source as well](https://github.com/umbraco/UmbracoDocs) and we're happy to look at your documentation contributions.
## Join the Umbraco community
Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com/). Our Umbraco features forums for questions and answers, documentation, downloadable plugins for Umbraco, and a rich collection of community resources.
Besides "Our", we all support each other also via Twitter: [Umbraco HQ](https://twitter.com/umbraco), [Release Updates](https://twitter.com/umbracoproject), [#umbraco](https://twitter.com/hashtag/umbraco)
## Contributing
Umbraco is contribution-focused and community-driven. If you want to contribute back to the Umbraco source code, please check out our [guide to contributing](CONTRIBUTING.md).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 17 KiB

+1
View File
@@ -157,6 +157,7 @@ build.tmp/
build/hooks/
build/temp/
/src/Umbraco.Web.UI.Client/src/common/**/build/temp/
# eof
-1
View File
@@ -22,7 +22,6 @@
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="AutoMapper" version="[8.0.0,8.999999)" />
<dependency id="LightInject" version="[5.4.0,5.999999)" />
<dependency id="LightInject.Annotation" version="[1.1.0,1.999999)" />
<dependency id="LightInject.Web" version="[2.0.0,2.999999)" />
+9 -4
View File
@@ -1,10 +1,10 @@
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using Umbraco.Core;
[assembly: PreApplicationStartMethod(typeof(BindingRedirects), "Initialize")]
// no binding redirect for now = de-activate
//[assembly: PreApplicationStartMethod(typeof(BindingRedirects), "Initialize")]
namespace Umbraco.Core
{
@@ -18,7 +18,7 @@ namespace Umbraco.Core
// this only gets called when an assembly can't be resolved
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
/// <summary>
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
/// </summary>
@@ -27,14 +27,19 @@ namespace Umbraco.Core
/// <returns></returns>
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// When an assembly can't be resolved. In here we can do magic with the assembly name and try loading another.
// keep here for reference - we don't use AutoMapper
/*
//AutoMapper:
// this is used for loading a signed assembly of AutoMapper (v. 3.1+) without having to recompile old code.
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null"))
return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005"));
*/
return null;
}
}
}
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
@@ -13,11 +12,6 @@ namespace Umbraco.Core.Cache
public static NoCacheRepositoryCachePolicy<TEntity, TId> Instance { get; } = new NoCacheRepositoryCachePolicy<TEntity, TId>();
public IRepositoryCachePolicy<TEntity, TId> Scoped(IAppPolicyCache runtimeCache, IScope scope)
{
throw new NotImplementedException();
}
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performGet(id);
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Compose
private static void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContent.ToInvariantString())))
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString)))
{
var relationService = Current.Services.RelationService;
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
@@ -37,7 +37,7 @@ namespace Umbraco.Core.Compose
private static void MediaService_Moved(IMediaService sender, MoveEventArgs<IMedia> e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMedia.ToInvariantString())))
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMediaString)))
{
var relationService = Current.Services.RelationService;
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
@@ -1,4 +1,4 @@
using AutoMapper;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Composing.CompositionExtensions
@@ -8,7 +8,9 @@ namespace Umbraco.Core.Composing.CompositionExtensions
{
public static Composition ComposeCoreMappingProfiles(this Composition composition)
{
composition.Register<Profile, IdentityMapperProfile>();
composition.RegisterUnique<UmbracoMapper>();
composition.WithCollectionBuilder<MapDefinitionCollectionBuilder>()
.Add<IdentityMapDefinition>();
return composition;
}
}
@@ -46,6 +46,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
composition.RegisterUnique<IPartialViewRepository, PartialViewRepository>();
composition.RegisterUnique<IScriptRepository, ScriptRepository>();
composition.RegisterUnique<IStylesheetRepository, StylesheetRepository>();
composition.RegisterUnique<IContentTypeCommonRepository, ContentTypeCommonRepository>();
return composition;
}
+4
View File
@@ -4,6 +4,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Dictionary;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PackageActions;
using Umbraco.Core.Packaging;
@@ -103,6 +104,9 @@ namespace Umbraco.Core.Composing
#region Getters
public static UmbracoMapper Mapper
=> _factory.GetInstance<UmbracoMapper>();
public static IShortStringHelper ShortStringHelper
=> _shortStringHelper ?? (_shortStringHelper = _factory?.TryGetInstance<IShortStringHelper>()
?? new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(Configs.Settings())));
@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Implements an un-ordered collection builder.
/// </summary>
/// <typeparam name="TBuilder">The type of the builder.</typeparam>
/// <typeparam name="TCollection">The type of the collection.</typeparam>
/// <typeparam name="TItem">The type of the items.</typeparam>
/// <remarks>
/// <para>A set collection builder is the most basic collection builder,
/// where items are not ordered.</para>
/// </remarks>
public abstract class SetCollectionBuilderBase<TBuilder, TCollection, TItem> : CollectionBuilderBase<TBuilder, TCollection, TItem>
where TBuilder : SetCollectionBuilderBase<TBuilder, TCollection, TItem>
where TCollection : class, IBuilderCollection<TItem>
{
protected abstract TBuilder This { get; }
/// <summary>
/// Clears all types in the collection.
/// </summary>
/// <returns>The builder.</returns>
public TBuilder Clear()
{
Configure(types => types.Clear());
return This;
}
/// <summary>
/// Adds a type to the collection.
/// </summary>
/// <typeparam name="T">The type to append.</typeparam>
/// <returns>The builder.</returns>
public TBuilder Add<T>()
where T : TItem
{
Configure(types =>
{
var type = typeof(T);
if (types.Contains(type)) types.Remove(type);
types.Add(type);
});
return This;
}
/// <summary>
/// Adds a type to the collection.
/// </summary>
/// <param name="type">The type to append.</param>
/// <returns>The builder.</returns>
public TBuilder Add(Type type)
{
Configure(types =>
{
EnsureType(type, "register");
if (types.Contains(type)) types.Remove(type);
types.Add(type);
});
return This;
}
/// <summary>
/// Adds types to the collections.
/// </summary>
/// <param name="types">The types to append.</param>
/// <returns>The builder.</returns>
public TBuilder Add(IEnumerable<Type> types)
{
Configure(list =>
{
foreach (var type in types)
{
// would be detected by CollectionBuilderBase when registering, anyways, but let's fail fast
EnsureType(type, "register");
if (list.Contains(type)) list.Remove(type);
list.Add(type);
}
});
return This;
}
/// <summary>
/// Removes a type from the collection.
/// </summary>
/// <typeparam name="T">The type to remove.</typeparam>
/// <returns>The builder.</returns>
public TBuilder Remove<T>()
where T : TItem
{
Configure(types =>
{
var type = typeof(T);
if (types.Contains(type)) types.Remove(type);
});
return This;
}
/// <summary>
/// Removes a type from the collection.
/// </summary>
/// <param name="type">The type to remove.</param>
/// <returns>The builder.</returns>
public TBuilder Remove(Type type)
{
Configure(types =>
{
EnsureType(type, "remove");
if (types.Contains(type)) types.Remove(type);
});
return This;
}
/// <summary>
/// Replaces a type in the collection.
/// </summary>
/// <typeparam name="TReplaced">The type to replace.</typeparam>
/// <typeparam name="T">The type to insert.</typeparam>
/// <returns>The builder.</returns>
/// <remarks>Throws if the type to replace does not already belong to the collection.</remarks>
public TBuilder Replace<TReplaced, T>()
where TReplaced : TItem
where T : TItem
{
Configure(types =>
{
var typeReplaced = typeof(TReplaced);
var type = typeof(T);
if (typeReplaced == type) return;
var index = types.IndexOf(typeReplaced);
if (index < 0) throw new InvalidOperationException();
if (types.Contains(type)) types.Remove(type);
index = types.IndexOf(typeReplaced); // in case removing type changed index
types.Insert(index, type);
types.Remove(typeReplaced);
});
return This;
}
/// <summary>
/// Replaces a type in the collection.
/// </summary>
/// <param name="typeReplaced">The type to replace.</param>
/// <param name="type">The type to insert.</param>
/// <returns>The builder.</returns>
/// <remarks>Throws if the type to replace does not already belong to the collection.</remarks>
public TBuilder Replace(Type typeReplaced, Type type)
{
Configure(types =>
{
EnsureType(typeReplaced, "find");
EnsureType(type, "register");
if (typeReplaced == type) return;
var index = types.IndexOf(typeReplaced);
if (index < 0) throw new InvalidOperationException();
if (types.Contains(type)) types.Remove(type);
index = types.IndexOf(typeReplaced); // in case removing type changed index
types.Insert(index, type);
types.Remove(typeReplaced);
});
return This;
}
}
}
+22
View File
@@ -203,6 +203,28 @@ namespace Umbraco.Core
composition.RegisterUnique(_ => registrar);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating the options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, Func<IFactory, DatabaseServerMessengerOptions> factory)
{
composition.RegisterUnique(factory);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="options">Options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, DatabaseServerMessengerOptions options)
{
composition.RegisterUnique(_ => options);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
@@ -93,7 +93,6 @@ namespace Umbraco.Core
/// </summary>
public const string DisableElectionForSingleServer = "Umbraco.Core.DisableElectionForSingleServer";
/// <summary>
/// Debug specific web.config AppSetting keys for Umbraco
/// </summary>
+2 -2
View File
@@ -145,11 +145,11 @@
public const string PartialViewMacros = "partialViewMacros";
public const string LogViewer = "logViewer";
public const string LogViewer = "logViewer";
public static class Groups
{
public const string Settings = "settingsGroup";
public const string Settings = "settingsGroup";
public const string Templating = "templatingGroup";
+66 -66
View File
@@ -92,10 +92,10 @@ namespace Umbraco.Core
/// </summary>
public const string Extension = "umbracoExtension";
/// <summary>
/// The default height/width of an image file if the size can't be determined from the metadata
/// </summary>
public const int DefaultSize = 200;
/// <summary>
/// The default height/width of an image file if the size can't be determined from the metadata
/// </summary>
public const int DefaultSize = 200;
}
/// <summary>
@@ -209,71 +209,71 @@ namespace Umbraco.Core
public static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
{
return new Dictionary<string, PropertyType>
{
{
Comments,
new PropertyType(PropertyEditors.Aliases.TextArea, ValueStorageType.Ntext, true, Comments)
{
Comments,
new PropertyType(PropertyEditors.Aliases.TextArea, ValueStorageType.Ntext, true, Comments)
{
Name = CommentsLabel
}
},
{
FailedPasswordAttempts,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts)
{
Name = FailedPasswordAttemptsLabel
}
},
{
IsApproved,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsApproved)
{
Name = IsApprovedLabel
}
},
{
IsLockedOut,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsLockedOut)
{
Name = IsLockedOutLabel
}
},
{
LastLockoutDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
{
Name = LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
{
Name = LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
{
Name = LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
{
Name = PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
{
Name = PasswordQuestionLabel
}
Name = CommentsLabel
}
};
},
{
FailedPasswordAttempts,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts)
{
Name = FailedPasswordAttemptsLabel
}
},
{
IsApproved,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsApproved)
{
Name = IsApprovedLabel
}
},
{
IsLockedOut,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsLockedOut)
{
Name = IsLockedOutLabel
}
},
{
LastLockoutDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
{
Name = LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
{
Name = LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
{
Name = LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
{
Name = PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
{
Name = PasswordQuestionLabel
}
}
};
}
}
+1 -11
View File
@@ -1,17 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Core
namespace Umbraco.Core
{
public static partial class Constants
{
public static class Icons
{
/// <summary>
/// System contenttype icon
/// </summary>
@@ -42,12 +34,10 @@ namespace Umbraco.Core
/// </summary>
public const string MemberType = "icon-users";
/// <summary>
/// System member icon
/// </summary>
public const string Template = "icon-layout";
}
}
}
+1 -4
View File
@@ -1,7 +1,4 @@
using System;
using System.ComponentModel;
namespace Umbraco.Core
namespace Umbraco.Core
{
public static partial class Constants
{
@@ -123,7 +123,6 @@ namespace Umbraco.Core
public static readonly Guid Template = new Guid(Strings.Template);
public static readonly Guid ContentItem = new Guid(Strings.ContentItem);
}
}
}
@@ -3,7 +3,7 @@
public static partial class Constants
{
/// <summary>
/// Defines the constants used for the Umbraco package repository
/// Defines the constants used for the Umbraco package repository
/// </summary>
public static class PackageRepository
{
@@ -34,7 +34,6 @@ namespace Umbraco.Core
/// </summary>
public const string ContentPicker = "Umbraco.ContentPicker";
/// <summary>
/// DateTime.
/// </summary>
+1 -5
View File
@@ -1,7 +1,4 @@
using System;
using System.ComponentModel;
namespace Umbraco.Core
namespace Umbraco.Core
{
public static partial class Constants
{
@@ -22,7 +19,6 @@ namespace Umbraco.Core
public const string PreviewCookieName = "UMB_PREVIEW";
public const string InstallerCookieName = "umb_installId";
}
}
}
+11 -5
View File
@@ -7,6 +7,7 @@ using System.Web;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPoco.Expressions;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
@@ -52,8 +53,8 @@ namespace Umbraco.Core
return ContentStatus.Unpublished;
}
#endregion
/// <summary>
@@ -134,9 +135,14 @@ namespace Umbraco.Core
/// <summary>
/// Sets the posted file value of a property.
/// </summary>
/// <remarks>This really is for FileUpload fields only, and should be obsoleted. For anything else,
/// you need to store the file by yourself using Store and then figure out
/// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself.</remarks>
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, HttpPostedFileBase postedFile, string culture = null, string segment = null)
{
content.SetValue(contentTypeBaseServiceProvider, propertyTypeAlias, postedFile.FileName, postedFile.InputStream, culture, segment);
}
/// <summary>
/// Sets the posted file value of a property.
/// </summary>
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null)
{
if (filename == null || filestream == null) return;
@@ -0,0 +1,13 @@
namespace Umbraco.Core.Mapping
{
/// <summary>
/// Defines maps for <see cref="UmbracoMapper"/>.
/// </summary>
public interface IMapDefinition
{
/// <summary>
/// Defines maps.
/// </summary>
void DefineMaps(UmbracoMapper mapper);
}
}
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Mapping
{
public class MapDefinitionCollection : BuilderCollectionBase<IMapDefinition>
{
public MapDefinitionCollection(IEnumerable<IMapDefinition> items)
: base(items)
{ }
}
}
@@ -0,0 +1,11 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Mapping
{
public class MapDefinitionCollectionBuilder : SetCollectionBuilderBase<MapDefinitionCollectionBuilder, MapDefinitionCollection, IMapDefinition>
{
protected override MapDefinitionCollectionBuilder This => this;
protected override Lifetime CollectionLifetime => Lifetime.Transient;
}
}
+130
View File
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Mapping
{
/// <summary>
/// Represents a mapper context.
/// </summary>
public class MapperContext
{
private readonly UmbracoMapper _mapper;
private IDictionary<string, object> _items;
/// <summary>
/// Initializes a new instance of the <see cref="MapperContext"/> class.
/// </summary>
public MapperContext(UmbracoMapper mapper)
{
_mapper = mapper;
}
/// <summary>
/// Gets a value indicating whether the context has items.
/// </summary>
public bool HasItems => _items != null;
/// <summary>
/// Gets the context items.
/// </summary>
public IDictionary<string, object> Items => _items ?? (_items = new Dictionary<string, object>());
#region Map
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <returns>The target object.</returns>
public TTarget Map<TTarget>(object source)
=> _mapper.Map<TTarget>(source, this);
// let's say this is a bad (dangerous) idea, and leave it out for now
/*
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>The target object.</returns>
public TTarget Map<TTarget>(object source, Action<MapperContext> f)
{
f(this);
return _mapper.Map<TTarget>(source, this);
}
*/
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source)
=> _mapper.Map<TSource, TTarget>(source, this);
// let's say this is a bad (dangerous) idea, and leave it out for now
/*
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, Action<MapperContext> f)
{
f(this);
return _mapper.Map<TSource, TTarget>(source, this);
}
*/
/// <summary>
/// Maps a source object to an existing target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="target">The target object.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, TTarget target)
=> _mapper.Map(source, target, this);
// let's say this is a bad (dangerous) idea, and leave it out for now
/*
/// <summary>
/// Maps a source object to an existing target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="target">The target object.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, TTarget target, Action<MapperContext> f)
{
f(this);
return _mapper.Map(source, target, this);
}
*/
/// <summary>
/// Maps an enumerable of source objects to a new list of target objects.
/// </summary>
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
/// <param name="source">The source objects.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
{
return source.Select(Map<TSourceElement, TTargetElement>).ToList();
}
#endregion
}
}
+424
View File
@@ -0,0 +1,424 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Mapping
{
// notes:
// AutoMapper maps null to empty arrays, lists, etc
// TODO:
// when mapping from TSource, and no map is found, consider the actual source.GetType()?
// when mapping to TTarget, and no map is found, consider the actual target.GetType()?
// not sure we want to add magic to this simple mapper class, though
/// <summary>
/// Umbraco Mapper.
/// </summary>
/// <remarks>
/// <para>When a map is defined from TSource to TTarget, the mapper automatically knows how to map
/// from IEnumerable{TSource} to IEnumerable{TTarget} (using a List{TTarget}) and to TTarget[].</para>
/// <para>When a map is defined from TSource to TTarget, the mapper automatically uses that map
/// for any source type that inherits from, or implements, TSource.</para>
/// <para>When a map is defined from TSource to TTarget, the mapper can map to TTarget exclusively
/// and cannot re-use that map for types that would inherit from, or implement, TTarget.</para>
/// <para>When using the Map{TSource, TTarget}(TSource source, ...) overloads, TSource is explicit. When
/// using the Map{TTarget}(object source, ...) TSource is defined as source.GetType().</para>
/// <para>In both cases, TTarget is explicit and not typeof(target).</para>
/// </remarks>
public class UmbracoMapper
{
private readonly Dictionary<Type, Dictionary<Type, Func<object, MapperContext, object>>> _ctors
= new Dictionary<Type, Dictionary<Type, Func<object, MapperContext, object>>>();
private readonly Dictionary<Type, Dictionary<Type, Action<object, object, MapperContext>>> _maps
= new Dictionary<Type, Dictionary<Type, Action<object, object, MapperContext>>>();
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoMapper"/> class.
/// </summary>
/// <param name="profiles"></param>
public UmbracoMapper(MapDefinitionCollection profiles)
{
foreach (var profile in profiles)
profile.DefineMaps(this);
}
#region Define
private static TTarget ThrowCtor<TSource, TTarget>(TSource source, MapperContext context)
=> throw new InvalidOperationException($"Don't know how to create {typeof(TTarget).FullName} instances.");
private static void Identity<TSource, TTarget>(TSource source, TTarget target, MapperContext context)
{ }
/// <summary>
/// Defines a mapping.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
public void Define<TSource, TTarget>()
=> Define<TSource, TTarget>(ThrowCtor<TSource, TTarget>, Identity);
/// <summary>
/// Defines a mapping.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="map">A mapping method.</param>
public void Define<TSource, TTarget>(Action<TSource, TTarget, MapperContext> map)
=> Define(ThrowCtor<TSource, TTarget>, map);
/// <summary>
/// Defines a mapping.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="ctor">A constructor method.</param>
public void Define<TSource, TTarget>(Func<TSource, MapperContext, TTarget> ctor)
=> Define(ctor, Identity);
/// <summary>
/// Defines a mapping.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="ctor">A constructor method.</param>
/// <param name="map">A mapping method.</param>
public void Define<TSource, TTarget>(Func<TSource, MapperContext, TTarget> ctor, Action<TSource, TTarget, MapperContext> map)
{
var sourceType = typeof(TSource);
var targetType = typeof(TTarget);
var sourceCtors = DefineCtors(sourceType);
if (ctor != null)
sourceCtors[targetType] = (source, context) => ctor((TSource)source, context);
var sourceMaps = DefineMaps(sourceType);
sourceMaps[targetType] = (source, target, context) => map((TSource)source, (TTarget)target, context);
}
private Dictionary<Type, Func<object, MapperContext, object>> DefineCtors(Type sourceType)
{
if (!_ctors.TryGetValue(sourceType, out var sourceCtor))
sourceCtor = _ctors[sourceType] = new Dictionary<Type, Func<object, MapperContext, object>>();
return sourceCtor;
}
private Dictionary<Type, Action<object, object, MapperContext>> DefineMaps(Type sourceType)
{
if (!_maps.TryGetValue(sourceType, out var sourceMap))
sourceMap = _maps[sourceType] = new Dictionary<Type, Action<object, object, MapperContext>>();
return sourceMap;
}
#endregion
#region Map
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <returns>The target object.</returns>
public TTarget Map<TTarget>(object source)
=> Map<TTarget>(source, new MapperContext(this));
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>The target object.</returns>
public TTarget Map<TTarget>(object source, Action<MapperContext> f)
{
var context = new MapperContext(this);
f(context);
return Map<TTarget>(source, context);
}
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="context">A mapper context.</param>
/// <returns>The target object.</returns>
public TTarget Map<TTarget>(object source, MapperContext context)
=> Map<TTarget>(source, source?.GetType(), context);
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source)
=> Map<TSource, TTarget>(source, new MapperContext(this));
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, Action<MapperContext> f)
{
var context = new MapperContext(this);
f(context);
return Map<TSource, TTarget>(source, context);
}
/// <summary>
/// Maps a source object to a new target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="context">A mapper context.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, MapperContext context)
=> Map<TTarget>(source, typeof(TSource), context);
private TTarget Map<TTarget>(object source, Type sourceType, MapperContext context)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
var targetType = typeof(TTarget);
var ctor = GetCtor(sourceType, targetType);
var map = GetMap(sourceType, targetType);
// if there is a direct constructor, map
if (ctor != null && map != null)
{
var target = ctor(source, context);
map(source, target, context);
return (TTarget)target;
}
// otherwise, see if we can deal with enumerable
var ienumerableOfT = typeof(IEnumerable<>);
bool IsIEnumerableOfT(Type type) =>
type.IsGenericType &&
type.GenericTypeArguments.Length == 1 &&
type.GetGenericTypeDefinition() == ienumerableOfT;
// try to get source as an IEnumerable<T>
var sourceIEnumerable = IsIEnumerableOfT(sourceType) ? sourceType : sourceType.GetInterfaces().FirstOrDefault(IsIEnumerableOfT);
// if source is an IEnumerable<T> and target is T[] or IEnumerable<T>, we can create a map
if (sourceIEnumerable != null && IsEnumerableOrArrayOfType(targetType))
{
var sourceGenericArg = sourceIEnumerable.GenericTypeArguments[0];
var targetGenericArg = GetEnumerableOrArrayTypeArgument(targetType);
ctor = GetCtor(sourceGenericArg, targetGenericArg);
map = GetMap(sourceGenericArg, targetGenericArg);
// if there is a constructor for the underlying type, create & invoke the map
if (ctor != null && map != null)
{
// register (for next time) and do it now (for this time)
object NCtor(object s, MapperContext c) => MapEnumerableInternal<TTarget>((IEnumerable) s, targetGenericArg, ctor, map, c);
DefineCtors(sourceType)[targetType] = NCtor;
DefineMaps(sourceType)[targetType] = Identity;
return (TTarget) NCtor(source, context);
}
throw new InvalidOperationException($"Don't know how to map {sourceGenericArg.FullName} to {targetGenericArg.FullName}, so don't know how to map {sourceType.FullName} to {targetType.FullName}.");
}
throw new InvalidOperationException($"Don't know how to map {sourceType.FullName} to {targetType.FullName}.");
}
private TTarget MapEnumerableInternal<TTarget>(IEnumerable source, Type targetGenericArg, Func<object, MapperContext, object> ctor, Action<object, object, MapperContext> map, MapperContext context)
{
var targetList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(targetGenericArg));
foreach (var sourceItem in source)
{
var targetItem = ctor(sourceItem, context);
map(sourceItem, targetItem, context);
targetList.Add(targetItem);
}
object target = targetList;
if (typeof(TTarget).IsArray)
{
var elementType = typeof(TTarget).GetElementType();
if (elementType == null) throw new Exception("panic");
var targetArray = Array.CreateInstance(elementType, targetList.Count);
targetList.CopyTo(targetArray, 0);
target = targetArray;
}
return (TTarget) target;
}
/// <summary>
/// Maps a source object to an existing target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="target">The target object.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, TTarget target)
=> Map(source, target, new MapperContext(this));
/// <summary>
/// Maps a source object to an existing target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="target">The target object.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, TTarget target, Action<MapperContext> f)
{
var context = new MapperContext(this);
f(context);
return Map(source, target, context);
}
/// <summary>
/// Maps a source object to an existing target object.
/// </summary>
/// <typeparam name="TSource">The source type.</typeparam>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <param name="source">The source object.</param>
/// <param name="target">The target object.</param>
/// <param name="context">A mapper context.</param>
/// <returns>The target object.</returns>
public TTarget Map<TSource, TTarget>(TSource source, TTarget target, MapperContext context)
{
var sourceType = typeof(TSource);
var targetType = typeof(TTarget);
var map = GetMap(sourceType, targetType);
// if there is a direct map, map
if (map != null)
{
map(source, target, context);
return target;
}
// we cannot really map to an existing enumerable - give up
throw new InvalidOperationException($"Don't know how to map {typeof(TSource).FullName} to {typeof(TTarget).FullName}.");
}
private Func<object, MapperContext, object> GetCtor(Type sourceType, Type targetType)
{
if (_ctors.TryGetValue(sourceType, out var sourceCtor) && sourceCtor.TryGetValue(targetType, out var ctor))
return ctor;
ctor = null;
foreach (var (stype, sctors) in _ctors)
{
if (!stype.IsAssignableFrom(sourceType)) continue;
if (!sctors.TryGetValue(targetType, out ctor)) continue;
sourceCtor = sctors;
break;
}
if (ctor == null) return null;
_ctors[sourceType] = sourceCtor;
return ctor;
}
private Action<object, object, MapperContext> GetMap(Type sourceType, Type targetType)
{
if (_maps.TryGetValue(sourceType, out var sourceMap) && sourceMap.TryGetValue(targetType, out var map))
return map;
map = null;
foreach (var (stype, smap) in _maps)
{
if (!stype.IsAssignableFrom(sourceType)) continue;
// TODO: consider looking for assignable types for target too?
if (!smap.TryGetValue(targetType, out map)) continue;
sourceMap = smap;
break;
}
if (map == null) return null;
_maps[sourceType] = sourceMap;
return map;
}
private static bool IsEnumerableOrArrayOfType(Type type)
{
if (type.IsArray && type.GetArrayRank() == 1) return true;
if (type.IsGenericType && type.GenericTypeArguments.Length == 1) return true;
return false;
}
private static Type GetEnumerableOrArrayTypeArgument(Type type)
{
if (type.IsArray) return type.GetElementType();
if (type.IsGenericType) return type.GenericTypeArguments[0];
throw new Exception("panic");
}
/// <summary>
/// Maps an enumerable of source objects to a new list of target objects.
/// </summary>
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
/// <param name="source">The source objects.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
{
return source.Select(Map<TSourceElement, TTargetElement>).ToList();
}
/// <summary>
/// Maps an enumerable of source objects to a new list of target objects.
/// </summary>
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
/// <param name="source">The source objects.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, Action<MapperContext> f)
{
var context = new MapperContext(this);
f(context);
return source.Select(x => Map<TSourceElement, TTargetElement>(x, context)).ToList();
}
/// <summary>
/// Maps an enumerable of source objects to a new list of target objects.
/// </summary>
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
/// <param name="source">The source objects.</param>
/// <param name="context">A mapper context.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, MapperContext context)
{
return source.Select(x => Map<TSourceElement, TTargetElement>(x, context)).ToList();
}
#endregion
}
}
@@ -47,7 +47,7 @@ namespace Umbraco.Core.Migrations.Install
typeof (LogDto),
typeof (MacroDto),
typeof (MacroPropertyDto),
typeof (MemberTypeDto),
typeof (MemberPropertyTypeDto),
typeof (MemberDto),
typeof (Member2MemberGroupDto),
typeof (PropertyTypeGroupDto),
@@ -13,8 +13,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_9_0
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.MemberType) && x.ColumnName.InvariantEquals("isSensitive")) == false)
AddColumn<MemberTypeDto>("isSensitive");
if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.MemberPropertyType) && x.ColumnName.InvariantEquals("isSensitive")) == false)
AddColumn<MemberPropertyTypeDto>("isSensitive");
}
}
}
@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Collections;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
@@ -170,23 +166,23 @@ namespace Umbraco.Core.Models
/// Sets the publishing values for names and properties.
/// </summary>
/// <param name="content"></param>
/// <param name="culture"></param>
/// <param name="impact"></param>
/// <returns>A value indicating whether it was possible to publish the names and values for the specified
/// culture(s). The method may fail if required names are not set, but it does NOT validate property data</returns>
public static bool PublishCulture(this IContent content, string culture = "*")
public static bool PublishCulture(this IContent content, CultureImpact impact)
{
culture = culture.NullOrWhiteSpaceAsNull();
if (impact == null) throw new ArgumentNullException(nameof(impact));
// the variation should be supported by the content type properties
// if the content type is invariant, only '*' and 'null' is ok
// if the content type varies, everything is ok because some properties may be invariant
if (!content.ContentType.SupportsPropertyVariation(culture, "*", true))
throw new NotSupportedException($"Culture \"{culture}\" is not supported by content type \"{content.ContentType.Alias}\" with variation \"{content.ContentType.Variations}\".");
if (!content.ContentType.SupportsPropertyVariation(impact.Culture, "*", true))
throw new NotSupportedException($"Culture \"{impact.Culture}\" is not supported by content type \"{content.ContentType.Alias}\" with variation \"{content.ContentType.Variations}\".");
var alsoInvariant = false;
if (culture == "*") // all cultures
// set names
if (impact.ImpactsAllCultures)
{
foreach (var c in content.AvailableCultures)
foreach (var c in content.AvailableCultures) // does NOT contain the invariant culture
{
var name = content.GetCultureName(c);
if (string.IsNullOrWhiteSpace(name))
@@ -194,26 +190,31 @@ namespace Umbraco.Core.Models
content.SetPublishInfo(c, name, DateTime.Now);
}
}
else if (culture == null) // invariant culture
else if (impact.ImpactsOnlyInvariantCulture)
{
if (string.IsNullOrWhiteSpace(content.Name))
return false;
// PublishName set by repository - nothing to do here
}
else // one single culture
else if (impact.ImpactsExplicitCulture)
{
var name = content.GetCultureName(culture);
var name = content.GetCultureName(impact.Culture);
if (string.IsNullOrWhiteSpace(name))
return false;
content.SetPublishInfo(culture, name, DateTime.Now);
alsoInvariant = true; // we also want to publish invariant values
content.SetPublishInfo(impact.Culture, name, DateTime.Now);
}
// property.PublishValues only publishes what is valid, variation-wise
// set values
// property.PublishValues only publishes what is valid, variation-wise,
// but accepts any culture arg: null, all, specific
foreach (var property in content.Properties)
{
property.PublishValues(culture);
if (alsoInvariant)
// for the specified culture (null or all or specific)
property.PublishValues(impact.Culture);
// maybe the specified culture did not impact the invariant culture, so PublishValues
// above would skip it, yet it *also* impacts invariant properties
if (impact.ImpactsAlsoInvariantProperties)
property.PublishValues(null);
}
+3 -12
View File
@@ -210,9 +210,7 @@ namespace Umbraco.Core.Models
return Variations.ValidateVariation(culture, segment, false, true, false);
}
/// <summary>
/// List of PropertyGroups available on this ContentType
/// </summary>
/// <inheritdoc />
/// <remarks>
/// <para>A PropertyGroup corresponds to a Tab in the UI</para>
/// <para>Marked DoNotClone because we will manually deal with cloning and the event handlers</para>
@@ -230,9 +228,7 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Gets all property types, across all property groups.
/// </summary>
/// <inheritdoc />
[IgnoreDataMember]
[DoNotClone]
public IEnumerable<PropertyType> PropertyTypes
@@ -243,12 +239,7 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Gets or sets the property types that are not in a group.
/// </summary>
/// <remarks>
/// Marked DoNotClone because we will manually deal with cloning and the event handlers
/// </remarks>
/// <inheritdoc />
[DoNotClone]
public IEnumerable<PropertyType> NoGroupPropertyTypes
{
@@ -43,9 +43,7 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Gets the property groups for the entire composition.
/// </summary>
/// <inheritdoc />
[IgnoreDataMember]
public IEnumerable<PropertyGroup> CompositionPropertyGroups
{
@@ -76,9 +74,7 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Gets the property types for the entire composition.
/// </summary>
/// <inheritdoc />
[IgnoreDataMember]
public IEnumerable<PropertyType> CompositionPropertyTypes
{
+257
View File
@@ -0,0 +1,257 @@
using System;
using System.Linq;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents the impact of a culture set.
/// </summary>
/// <remarks>
/// <para>A set of cultures can be either all cultures (including the invariant culture), or
/// the invariant culture, or a specific culture.</para>
/// </remarks>
internal class CultureImpact
{
/// <summary>
/// Utility method to return the culture used for invariant property errors based on what cultures are being actively saved,
/// the default culture and the state of the current content item
/// </summary>
/// <param name="content"></param>
/// <param name="savingCultures"></param>
/// <param name="defaultCulture"></param>
/// <returns></returns>
public static string GetCultureForInvariantErrors(IContent content, string[] savingCultures, string defaultCulture)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (savingCultures == null) throw new ArgumentNullException(nameof(savingCultures));
if (savingCultures.Length == 0) throw new ArgumentException(nameof(savingCultures));
var cultureForInvariantErrors = savingCultures.Any(x => x.InvariantEquals(defaultCulture))
//the default culture is being flagged for saving so use it
? defaultCulture
//If the content has no published version, we need to affiliate validation with the first variant being saved.
//If the content has a published version we will not affiliate the validation with any culture (null)
: !content.Published ? savingCultures[0] : null;
return cultureForInvariantErrors;
}
/// <summary>
/// Initializes a new instance of the <see cref="CultureImpact"/> class.
/// </summary>
/// <param name="culture">The culture code.</param>
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
private CultureImpact(string culture, bool isDefault = false)
{
if (culture != null && culture.IsNullOrWhiteSpace())
throw new ArgumentException("Culture \"\" is not valid here.");
Culture = culture;
if ((culture == null || culture == "*") && isDefault)
throw new ArgumentException("The invariant or 'all' culture can not be the default culture.");
ImpactsOnlyDefaultCulture = isDefault;
}
/// <summary>
/// Gets the impact of 'all' cultures (including the invariant culture).
/// </summary>
public static CultureImpact All { get; } = new CultureImpact("*");
/// <summary>
/// Gets the impact of the invariant culture.
/// </summary>
public static CultureImpact Invariant { get; } = new CultureImpact(null);
/// <summary>
/// Creates an impact instance representing the impact of a specific culture.
/// </summary>
/// <param name="culture">The culture code.</param>
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
public static CultureImpact Explicit(string culture, bool isDefault)
{
if (culture == null)
throw new ArgumentException("Culture <null> is not explicit.");
if (culture.IsNullOrWhiteSpace())
throw new ArgumentException("Culture \"\" is not explicit.");
if (culture == "*")
throw new ArgumentException("Culture \"*\" is not explicit.");
return new CultureImpact(culture, isDefault);
}
/// <summary>
/// Creates an impact instance representing the impact of a culture set,
/// in the context of a content item variation.
/// </summary>
/// <param name="culture">The culture code.</param>
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
/// <param name="content">The content item.</param>
/// <remarks>
/// <para>Validates that the culture is compatible with the variation.</para>
/// </remarks>
public static CultureImpact Create(string culture, bool isDefault, IContent content)
{
// throws if not successful
TryCreate(culture, isDefault, content.ContentType.Variations, true, out var impact);
return impact;
}
/// <summary>
/// Tries to create an impact instance representing the impact of a culture set,
/// in the context of a content item variation.
/// </summary>
/// <param name="culture">The culture code.</param>
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
/// <param name="variation">A content variation.</param>
/// <param name="throwOnFail">A value indicating whether to throw if the impact cannot be created.</param>
/// <param name="impact">The impact if it could be created, otherwise null.</param>
/// <returns>A value indicating whether the impact could be created.</returns>
/// <remarks>
/// <para>Validates that the culture is compatible with the variation.</para>
/// </remarks>
internal static bool TryCreate(string culture, bool isDefault, ContentVariation variation, bool throwOnFail, out CultureImpact impact)
{
impact = null;
// if culture is invariant...
if (culture == null)
{
// ... then variation must not vary by culture ...
if (variation.VariesByCulture())
{
if (throwOnFail)
throw new InvalidOperationException("The invariant culture is not compatible with a varying variation.");
return false;
}
// ... and it cannot be default
if (isDefault)
{
if (throwOnFail)
throw new InvalidOperationException("The invariant culture can not be the default culture.");
return false;
}
impact = Invariant;
return true;
}
// if culture is 'all'...
if (culture == "*")
{
// ... it cannot be default
if (isDefault)
{
if (throwOnFail)
throw new InvalidOperationException("The 'all' culture can not be the default culture.");
return false;
}
// if variation does not vary by culture, then impact is invariant
impact = variation.VariesByCulture() ? All : Invariant;
return true;
}
// neither null nor "*" - cannot be the empty string
if (culture.IsNullOrWhiteSpace())
{
if (throwOnFail)
throw new ArgumentException("Cannot be the empty string.", nameof(culture));
return false;
}
// if culture is specific, then variation must vary
if (!variation.VariesByCulture())
{
if (throwOnFail)
throw new InvalidOperationException($"The variant culture {culture} is not compatible with an invariant variation.");
return false;
}
// return specific impact
impact = new CultureImpact(culture, isDefault);
return true;
}
/// <summary>
/// Gets the culture code.
/// </summary>
/// <remarks>
/// <para>Can be null (invariant) or * (all cultures) or a specific culture code.</para>
/// </remarks>
public string Culture { get; }
/// <summary>
/// Gets a value indicating whether this impact impacts all cultures, including,
/// indirectly, the invariant culture.
/// </summary>
public bool ImpactsAllCultures => Culture == "*";
/// <summary>
/// Gets a value indicating whether this impact impacts only the invariant culture,
/// directly, not because all cultures are impacted.
/// </summary>
public bool ImpactsOnlyInvariantCulture => Culture == null;
/// <summary>
/// Gets a value indicating whether this impact impacts an implicit culture.
/// </summary>
/// <remarks>And then it does not impact the invariant culture. The impacted
/// explicit culture could be the default culture.</remarks>
public bool ImpactsExplicitCulture => Culture != null && Culture != "*";
/// <summary>
/// Gets a value indicating whether this impact impacts the default culture, directly,
/// not because all cultures are impacted.
/// </summary>
public bool ImpactsOnlyDefaultCulture {get; }
/// <summary>
/// Gets a value indicating whether this impact impacts the invariant properties, either
/// directly, or because all cultures are impacted, or because the default culture is impacted.
/// </summary>
public bool ImpactsInvariantProperties => Culture == null || Culture == "*" || ImpactsOnlyDefaultCulture;
/// <summary>
/// Gets a value indicating whether this also impact impacts the invariant properties,
/// even though it does not impact the invariant culture, neither directly (ImpactsInvariantCulture)
/// nor indirectly (ImpactsAllCultures).
/// </summary>
public bool ImpactsAlsoInvariantProperties => !ImpactsOnlyInvariantCulture &&
!ImpactsAllCultures &&
ImpactsOnlyDefaultCulture;
public Behavior CultureBehavior
{
get
{
//null can only be invariant
if (Culture == null) return Behavior.InvariantCulture | Behavior.InvariantProperties;
// * is All which means its also invariant properties since this will include the default language
if (Culture == "*") return (Behavior.AllCultures | Behavior.InvariantProperties);
//else it's explicit
var result = Behavior.ExplicitCulture;
//if the explicit culture is the default, then the behavior is also InvariantProperties
if (ImpactsOnlyDefaultCulture)
result |= Behavior.InvariantProperties;
return result;
}
}
[Flags]
public enum Behavior : byte
{
AllCultures = 1,
InvariantCulture = 2,
ExplicitCulture = 4,
InvariantProperties = 8
}
}
}
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Models
/// * when the setter performs additional required logic other than just setting the underlying field
///
/// </remarks>
internal class DoNotCloneAttribute : Attribute
public class DoNotCloneAttribute : Attribute
{
}
+3 -3
View File
@@ -96,17 +96,17 @@ namespace Umbraco.Core.Models
IEnumerable<ContentTypeSort> AllowedContentTypes { get; set; }
/// <summary>
/// Gets or Sets a collection of Property Groups
/// Gets or sets the local property groups.
/// </summary>
PropertyGroupCollection PropertyGroups { get; set; }
/// <summary>
/// Gets all property types, across all property groups.
/// Gets all local property types belonging to a group, across all local property groups.
/// </summary>
IEnumerable<PropertyType> PropertyTypes { get; }
/// <summary>
/// Gets or sets the property types that are not in a group.
/// Gets or sets the local property types that do not belong to a group.
/// </summary>
IEnumerable<PropertyType> NoGroupPropertyTypes { get; set; }
@@ -41,7 +41,7 @@ namespace Umbraco.Core.Models.Identity
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(username));
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(culture));
var user = new BackOfficeIdentityUser();
var user = new BackOfficeIdentityUser(Array.Empty<IReadOnlyUserGroup>());
user.DisableChangeTracking();
user._userName = username;
user._email = email;
@@ -54,16 +54,19 @@ namespace Umbraco.Core.Models.Identity
return user;
}
private BackOfficeIdentityUser()
private BackOfficeIdentityUser(IReadOnlyUserGroup[] groups)
{
_startMediaIds = new int[] { };
_startContentIds = new int[] { };
_groups = new IReadOnlyUserGroup[] { };
_allowedSections = new string[] { };
_startMediaIds = Array.Empty<int>();
_startContentIds = Array.Empty<int>();
_allowedSections = Array.Empty<string>();
_culture = Current.Configs.Global().DefaultUILanguage; // TODO: inject
_groups = new IReadOnlyUserGroup[0];
// must initialize before setting groups
_roles = new ObservableCollection<IdentityUserRole<string>>();
_roles.CollectionChanged += _roles_CollectionChanged;
// use the property setters - they do more than just setting a field
Groups = groups;
}
/// <summary>
@@ -72,19 +75,10 @@ namespace Umbraco.Core.Models.Identity
/// <param name="userId"></param>
/// <param name="groups"></param>
public BackOfficeIdentityUser(int userId, IEnumerable<IReadOnlyUserGroup> groups)
: this(groups.ToArray())
{
_startMediaIds = new int[] { };
_startContentIds = new int[] { };
_groups = new IReadOnlyUserGroup[] { };
_allowedSections = new string[] { };
_culture = Current.Configs.Global().DefaultUILanguage; // TODO: inject
_groups = groups.ToArray();
_roles = new ObservableCollection<IdentityUserRole<string>>(_groups.Select(x => new IdentityUserRole<string>
{
RoleId = x.Alias,
UserId = userId.ToString()
}));
_roles.CollectionChanged += _roles_CollectionChanged;
// use the property setters - they do more than just setting a field
Id = userId;
}
/// <summary>
@@ -226,6 +220,8 @@ namespace Umbraco.Core.Models.Identity
//so they recalculate
_allowedSections = null;
_groups = value;
//now clear all roles and re-add them
_roles.CollectionChanged -= _roles_CollectionChanged;
_roles.Clear();
@@ -0,0 +1,80 @@
using System;
using Umbraco.Core.Configuration;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models.Identity
{
public class IdentityMapDefinition : IMapDefinition
{
private readonly ILocalizedTextService _textService;
private readonly IEntityService _entityService;
private readonly IGlobalSettings _globalSettings;
public IdentityMapDefinition(ILocalizedTextService textService, IEntityService entityService, IGlobalSettings globalSettings)
{
_textService = textService;
_entityService = entityService;
_globalSettings = globalSettings;
}
public void DefineMaps(UmbracoMapper mapper)
{
mapper.Define<IUser, BackOfficeIdentityUser>(
(source, context) =>
{
var target = new BackOfficeIdentityUser(source.Id, source.Groups);
target.DisableChangeTracking();
return target;
},
(source, target, context) =>
{
Map(source, target);
target.ResetDirtyProperties(true);
target.EnableChangeTracking();
});
}
// Umbraco.Code.MapAll -Id -Groups -LockoutEnabled -PhoneNumber -PhoneNumberConfirmed -TwoFactorEnabled
private void Map(IUser source, BackOfficeIdentityUser target)
{
// well, the ctor has been fixed
/*
// these two are already set in ctor but BackOfficeIdentityUser ctor is CompletelyBroken
target.Id = source.Id;
target.Groups = source.Groups.ToArray();
*/
target.CalculatedMediaStartNodeIds = source.CalculateMediaStartNodeIds(_entityService);
target.CalculatedContentStartNodeIds = source.CalculateContentStartNodeIds(_entityService);
target.Email = source.Email;
target.UserName = source.Username;
target.LastPasswordChangeDateUtc = source.LastPasswordChangeDate.ToUniversalTime();
target.LastLoginDateUtc = source.LastLoginDate.ToUniversalTime();
target.EmailConfirmed = source.EmailConfirmedDate.HasValue;
target.Name = source.Name;
target.AccessFailedCount = source.FailedPasswordAttempts;
target.PasswordHash = GetPasswordHash(source.RawPasswordValue);
target.StartContentIds = source.StartContentIds;
target.StartMediaIds = source.StartMediaIds;
target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); // project CultureInfo to string
target.IsApproved = source.IsApproved;
target.SecurityStamp = source.SecurityStamp;
target.LockoutEndDateUtc = source.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?) null;
// this was in AutoMapper but does not have a setter anyways
//target.AllowedSections = source.AllowedSections.ToArray(),
// these were marked as ignored for AutoMapper but don't have a setter anyways
//target.Logins =;
//target.Claims =;
//target.Roles =;
}
private static string GetPasswordHash(string storedPass)
{
return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass;
}
}
}
@@ -1,58 +0,0 @@
using System;
using System.Linq;
using AutoMapper;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models.Identity
{
public class IdentityMapperProfile : Profile
{
public IdentityMapperProfile(ILocalizedTextService textService, IEntityService entityService, IGlobalSettings globalSettings)
{
CreateMap<IUser, BackOfficeIdentityUser>()
.BeforeMap((src, dest) =>
{
dest.DisableChangeTracking();
})
.ConstructUsing(src => new BackOfficeIdentityUser(src.Id, src.Groups))
.ForMember(dest => dest.LastLoginDateUtc, opt => opt.MapFrom(src => src.LastLoginDate.ToUniversalTime()))
.ForMember(user => user.LastPasswordChangeDateUtc, expression => expression.MapFrom(user => user.LastPasswordChangeDate.ToUniversalTime()))
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email))
.ForMember(dest => dest.EmailConfirmed, opt => opt.MapFrom(src => src.EmailConfirmedDate.HasValue))
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.LockoutEndDateUtc, opt => opt.MapFrom(src => src.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?) null))
.ForMember(dest => dest.IsApproved, opt => opt.MapFrom(src => src.IsApproved))
.ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.Username))
.ForMember(dest => dest.PasswordHash, opt => opt.MapFrom(user => GetPasswordHash(user.RawPasswordValue)))
.ForMember(dest => dest.Culture, opt => opt.MapFrom(src => src.GetUserCulture(textService, globalSettings)))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.StartMediaIds, opt => opt.MapFrom(src => src.StartMediaIds))
.ForMember(dest => dest.StartContentIds, opt => opt.MapFrom(src => src.StartContentIds))
.ForMember(dest => dest.AccessFailedCount, opt => opt.MapFrom(src => src.FailedPasswordAttempts))
.ForMember(dest => dest.CalculatedContentStartNodeIds, opt => opt.MapFrom(src => src.CalculateContentStartNodeIds(entityService)))
.ForMember(dest => dest.CalculatedMediaStartNodeIds, opt => opt.MapFrom(src => src.CalculateMediaStartNodeIds(entityService)))
.ForMember(dest => dest.AllowedSections, opt => opt.MapFrom(src => src.AllowedSections.ToArray()))
.ForMember(dest => dest.LockoutEnabled, opt => opt.Ignore())
.ForMember(dest => dest.Logins, opt => opt.Ignore())
.ForMember(dest => dest.EmailConfirmed, opt => opt.Ignore())
.ForMember(dest => dest.PhoneNumber, opt => opt.Ignore())
.ForMember(dest => dest.PhoneNumberConfirmed, opt => opt.Ignore())
.ForMember(dest => dest.TwoFactorEnabled, opt => opt.Ignore())
.ForMember(dest => dest.Roles, opt => opt.Ignore())
.ForMember(dest => dest.Claims, opt => opt.Ignore())
.AfterMap((src, dest) =>
{
dest.ResetDirtyProperties(true);
dest.EnableChangeTracking();
});
}
private static string GetPasswordHash(string storedPass)
{
return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass;
}
}
}
+5 -5
View File
@@ -151,22 +151,22 @@ namespace Umbraco.Core.Models
internal static bool HasContentRootAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.Root.ToInvariantString(), user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RootString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
}
internal static bool HasContentBinAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinContent.ToInvariantString(), user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinContentString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
}
internal static bool HasMediaRootAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.Root.ToInvariantString(), user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RootString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
}
internal static bool HasMediaBinAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinMedia.ToInvariantString(), user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinMediaString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
}
internal static bool HasPathAccess(this IUser user, IContent content, IEntityService entityService)
@@ -327,7 +327,7 @@ namespace Umbraco.Core.Models
? entityService.GetAllPaths(objectType, asn).ToDictionary(x => x.Id, x => x.Path)
: new Dictionary<int, string>();
paths[Constants.System.Root] = Constants.System.Root.ToString(); // entityService does not get that one
paths[Constants.System.Root] = Constants.System.RootString; // entityService does not get that one
var binPath = GetBinPath(objectType);
@@ -57,7 +57,7 @@ namespace Umbraco.Core
public const string MacroProperty = /*TableNamePrefix*/ "cms" + "MacroProperty";
public const string Member = /*TableNamePrefix*/ "cms" + "Member";
public const string MemberType = /*TableNamePrefix*/ "cms" + "MemberType";
public const string MemberPropertyType = /*TableNamePrefix*/ "cms" + "MemberType";
public const string Member2MemberGroup = /*TableNamePrefix*/ "cms" + "Member2MemberGroup";
public const string Access = TableNamePrefix + "Access";
@@ -69,7 +69,7 @@ namespace Umbraco.Core
public const string Tag = /*TableNamePrefix*/ "cms" + "Tags";
public const string TagRelationship = /*TableNamePrefix*/ "cms" + "TagRelationship";
public const string KeyValue = TableNamePrefix + "KeyValue";
public const string AuditEntry = TableNamePrefix + "Audit";
@@ -54,6 +54,7 @@ namespace Umbraco.Core.Persistence.Dtos
public byte Variations { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne, ColumnName = "NodeId")]
public NodeDto NodeDto { get; set; }
}
}
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Dtos
public string Configuration { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne, ColumnName = "DataTypeId")]
[Reference(ReferenceType.OneToOne, ColumnName = "NodeId")]
public NodeDto NodeDto { get; set; }
}
}
@@ -3,10 +3,10 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Persistence.Dtos
{
[TableName(Constants.DatabaseSchema.Tables.MemberType)]
[TableName(Constants.DatabaseSchema.Tables.MemberPropertyType)]
[PrimaryKey("pk")]
[ExplicitColumns]
internal class MemberTypeDto
internal class MemberPropertyTypeDto
{
[Column("pk")]
[PrimaryKeyColumn]
@@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using NPoco;
namespace Umbraco.Core.Persistence.Dtos
{
[TableName(Constants.DatabaseSchema.Tables.Node)]
[PrimaryKey("id")]
[ExplicitColumns]
internal class MemberTypeReadOnlyDto
{
/* from umbracoNode */
[Column("id")]
public int NodeId { get; set; }
[Column("trashed")]
public bool Trashed { get; set; }
[Column("parentID")]
public int ParentId { get; set; }
[Column("nodeUser")]
public int? UserId { get; set; }
[Column("level")]
public short Level { get; set; }
[Column("path")]
public string Path { get; set; }
[Column("sortOrder")]
public int SortOrder { get; set; }
[Column("uniqueID")]
public Guid? UniqueId { get; set; }
[Column("text")]
public string Text { get; set; }
[Column("nodeObjectType")]
public Guid? NodeObjectType { get; set; }
[Column("createDate")]
public DateTime CreateDate { get; set; }
/* cmsContentType */
[Column("pk")]
public int PrimaryKey { get; set; }
[Column("alias")]
public string Alias { get; set; }
[Column("icon")]
public string Icon { get; set; }
[Column("thumbnail")]
public string Thumbnail { get; set; }
[Column("description")]
public string Description { get; set; }
[Column("isContainer")]
public bool IsContainer { get; set; }
[Column("allowAtRoot")]
public bool AllowAtRoot { get; set; }
/* PropertyTypes */
// TODO: Add PropertyTypeDto (+MemberTypeDto and DataTypeDto as one) ReadOnly list
[ResultColumn]
[Reference(ReferenceType.Many, ReferenceMemberName = "ContentTypeId")]
public List<PropertyTypeReadOnlyDto> PropertyTypes { get; set; }
/* PropertyTypeGroups */
// TODO: Add PropertyTypeGroupDto ReadOnly list
[ResultColumn]
[Reference(ReferenceType.Many, ReferenceMemberName = "ContentTypeNodeId")]
public List<PropertyTypeGroupReadOnlyDto> PropertyTypeGroups { get; set; }
}
}
@@ -0,0 +1,18 @@
using NPoco;
namespace Umbraco.Core.Persistence.Dtos
{
// this is PropertyTypeDto + the special property type fields for members
// it is used for querying everything needed for a property type, at once
internal class PropertyTypeCommonDto : PropertyTypeDto
{
[Column("memberCanEdit")]
public bool CanEdit { get; set; }
[Column("viewOnProfile")]
public bool ViewOnProfile { get; set; }
[Column("isSensitive")]
public bool IsSensitive { get; set; }
}
}
@@ -67,16 +67,28 @@ namespace Umbraco.Core.Persistence.Factories
public static IMemberType BuildMemberTypeEntity(ContentTypeDto dto)
{
throw new NotImplementedException();
var contentType = new MemberType(dto.NodeDto.ParentId);
try
{
contentType.DisableChangeTracking();
BuildCommonEntity(contentType, dto, false);
contentType.ResetDirtyProperties(false);
}
finally
{
contentType.EnableChangeTracking();
}
return contentType;
}
public static IEnumerable<MemberTypeDto> BuildMemberTypeDtos(IMemberType entity)
public static IEnumerable<MemberPropertyTypeDto> BuildMemberPropertyTypeDtos(IMemberType entity)
{
var memberType = entity as MemberType;
if (memberType == null || memberType.PropertyTypes.Any() == false)
return Enumerable.Empty<MemberTypeDto>();
return Enumerable.Empty<MemberPropertyTypeDto>();
var dtos = memberType.PropertyTypes.Select(x => new MemberTypeDto
var dtos = memberType.PropertyTypes.Select(x => new MemberPropertyTypeDto
{
NodeId = entity.Id,
PropertyTypeId = x.Id,
@@ -91,7 +103,7 @@ namespace Umbraco.Core.Persistence.Factories
#region Common
private static void BuildCommonEntity(ContentTypeBase entity, ContentTypeDto dto)
private static void BuildCommonEntity(ContentTypeBase entity, ContentTypeDto dto, bool setVariations = true)
{
entity.Id = dto.NodeDto.NodeId;
entity.Key = dto.NodeDto.UniqueId;
@@ -102,6 +114,7 @@ namespace Umbraco.Core.Persistence.Factories
entity.SortOrder = dto.NodeDto.SortOrder;
entity.Description = dto.Description;
entity.CreateDate = dto.NodeDto.CreateDate;
entity.UpdateDate = dto.NodeDto.CreateDate;
entity.Path = dto.NodeDto.Path;
entity.Level = dto.NodeDto.Level;
entity.CreatorId = dto.NodeDto.UserId ?? Constants.Security.UnknownUserId;
@@ -109,7 +122,9 @@ namespace Umbraco.Core.Persistence.Factories
entity.IsContainer = dto.IsContainer;
entity.IsElement = dto.IsElement;
entity.Trashed = dto.NodeDto.Trashed;
entity.Variations = (ContentVariation) dto.Variations;
if (setVariations)
entity.Variations = (ContentVariation) dto.Variations;
}
public static ContentTypeDto BuildContentTypeDto(IContentTypeBase entity)
@@ -1,194 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Repositories.Implement;
namespace Umbraco.Core.Persistence.Factories
{
internal static class MemberTypeReadOnlyFactory
{
public static IMemberType BuildEntity(MemberTypeReadOnlyDto dto, out bool needsSaving)
{
var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
needsSaving = false;
var memberType = new MemberType(dto.ParentId);
try
{
memberType.DisableChangeTracking();
memberType.Alias = dto.Alias;
memberType.AllowedAsRoot = dto.AllowAtRoot;
memberType.CreateDate = dto.CreateDate;
memberType.CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0;
memberType.Description = dto.Description;
memberType.Icon = dto.Icon;
memberType.Id = dto.NodeId;
memberType.IsContainer = dto.IsContainer;
memberType.Key = dto.UniqueId.Value;
memberType.Level = dto.Level;
memberType.Name = dto.Text;
memberType.Path = dto.Path;
memberType.SortOrder = dto.SortOrder;
memberType.Thumbnail = dto.Thumbnail;
memberType.Trashed = dto.Trashed;
memberType.UpdateDate = dto.CreateDate;
memberType.AllowedContentTypes = Enumerable.Empty<ContentTypeSort>();
var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType, standardPropertyTypes);
memberType.PropertyGroups = propertyTypeGroupCollection;
var propertyTypes = GetPropertyTypes(dto, memberType, standardPropertyTypes);
//By Convention we add 9 standard PropertyTypes - This is only here to support loading of types that didn't have these conventions before.
foreach (var standardPropertyType in standardPropertyTypes)
{
if (dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue;
// beware!
// means that we can return a memberType "from database" that has some property types
// that do *not* come from the database and therefore are incomplete eg have no key,
// no id, no dataTypeDefinitionId - ouch! - better notify caller of the situation
needsSaving = true;
//Add the standard PropertyType to the current list
propertyTypes.Add(standardPropertyType.Value);
//Internal dictionary for adding "MemberCanEdit", "VisibleOnProfile", "IsSensitive" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
new MemberTypePropertyProfileAccess(false, false, false));
}
memberType.NoGroupPropertyTypes = propertyTypes;
return memberType;
}
finally
{
memberType.EnableChangeTracking();
}
}
private static PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary<string, PropertyType> standardProps)
{
// see PropertyGroupFactory, repeating code here...
var propertyGroups = new PropertyGroupCollection();
foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue))
{
var group = new PropertyGroup(MemberType.SupportsPublishingConst);
// if the group is defined on the current member type,
// assign its identifier, else it will be zero
if (groupDto.ContentTypeNodeId == memberType.Id)
{
// note: no idea why Id is nullable here, but better check
if (groupDto.Id.HasValue == false)
throw new Exception("GroupDto.Id has no value.");
group.Id = groupDto.Id.Value;
}
group.Key = groupDto.UniqueId;
group.Name = groupDto.Text;
group.SortOrder = groupDto.SortOrder;
group.PropertyTypes = new PropertyTypeCollection(MemberType.SupportsPublishingConst);
//Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded
var localGroupDto = groupDto;
var typeDtos = dto.PropertyTypes.Where(x => x.Id.HasValue && x.Id > 0 && x.PropertyTypeGroupId.HasValue && x.PropertyTypeGroupId.Value == localGroupDto.Id.Value);
foreach (var typeDto in typeDtos)
{
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
var tempGroupDto = groupDto;
//ensures that any built-in membership properties have their correct dbtype assigned no matter
//what the underlying data type is
var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty(
typeDto.Alias,
typeDto.DbType.EnumParse<ValueStorageType>(true),
standardProps);
var propertyType = new PropertyType(
typeDto.PropertyEditorAlias,
propDbType.Result,
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
// which is what we want for the built-in properties.
propDbType.Success,
typeDto.Alias)
{
DataTypeId = typeDto.DataTypeId,
Description = typeDto.Description,
Id = typeDto.Id.Value,
Name = typeDto.Name,
Mandatory = typeDto.Mandatory,
SortOrder = typeDto.SortOrder,
ValidationRegExp = typeDto.ValidationRegExp,
PropertyGroupId = new Lazy<int>(() => tempGroupDto.Id.Value),
CreateDate = memberType.CreateDate,
UpdateDate = memberType.UpdateDate,
Key = typeDto.UniqueId
};
// reset dirty initial properties (U4-1946)
propertyType.ResetDirtyProperties(false);
group.PropertyTypes.Add(propertyType);
}
// reset dirty initial properties (U4-1946)
group.ResetDirtyProperties(false);
propertyGroups.Add(group);
}
return propertyGroups;
}
private static List<PropertyType> GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary<string, PropertyType> standardProps)
{
//Find PropertyTypes that does not belong to a PropertyTypeGroup
var propertyTypes = new List<PropertyType>();
foreach (var typeDto in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue))
{
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
//ensures that any built-in membership properties have their correct dbtype assigned no matter
//what the underlying data type is
var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty(
typeDto.Alias,
typeDto.DbType.EnumParse<ValueStorageType>(true),
standardProps);
var propertyType = new PropertyType(
typeDto.PropertyEditorAlias,
propDbType.Result,
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
// which is what we want for the built-in properties.
propDbType.Success,
typeDto.Alias)
{
DataTypeId = typeDto.DataTypeId,
Description = typeDto.Description,
Id = typeDto.Id.Value,
Mandatory = typeDto.Mandatory,
Name = typeDto.Name,
SortOrder = typeDto.SortOrder,
ValidationRegExp = typeDto.ValidationRegExp,
PropertyGroupId = null,
CreateDate = dto.CreateDate,
UpdateDate = dto.CreateDate,
Key = typeDto.UniqueId
};
propertyTypes.Add(propertyType);
}
return propertyTypes;
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -7,18 +8,18 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(PublicAccessEntry))]
public sealed class AccessMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public AccessMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<PublicAccessEntry, AccessDto>(src => src.Key, dto => dto.Id);
CacheMap<PublicAccessEntry, AccessDto>(src => src.LoginNodeId, dto => dto.LoginNodeId);
CacheMap<PublicAccessEntry, AccessDto>(src => src.NoAccessNodeId, dto => dto.NoAccessNodeId);
CacheMap<PublicAccessEntry, AccessDto>(src => src.ProtectedNodeId, dto => dto.NodeId);
CacheMap<PublicAccessEntry, AccessDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<PublicAccessEntry, AccessDto>(src => src.UpdateDate, dto => dto.UpdateDate);
DefineMap<PublicAccessEntry, AccessDto>(nameof(PublicAccessEntry.Key), nameof(AccessDto.Id));
DefineMap<PublicAccessEntry, AccessDto>(nameof(PublicAccessEntry.LoginNodeId), nameof(AccessDto.LoginNodeId));
DefineMap<PublicAccessEntry, AccessDto>(nameof(PublicAccessEntry.NoAccessNodeId), nameof(AccessDto.NoAccessNodeId));
DefineMap<PublicAccessEntry, AccessDto>(nameof(PublicAccessEntry.ProtectedNodeId), nameof(AccessDto.NodeId));
DefineMap<PublicAccessEntry, AccessDto>(nameof(PublicAccessEntry.CreateDate), nameof(AccessDto.CreateDate));
DefineMap<PublicAccessEntry, AccessDto>(nameof(PublicAccessEntry.UpdateDate), nameof(AccessDto.UpdateDate));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -11,21 +12,21 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(AuditEntry))]
public sealed class AuditEntryMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public AuditEntryMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.Id, dto => dto.Id);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingUserId, dto => dto.PerformingUserId);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingDetails, dto => dto.PerformingDetails);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingIp, dto => dto.PerformingIp);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventDateUtc, dto => dto.EventDateUtc);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.AffectedUserId, dto => dto.AffectedUserId);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.AffectedDetails, dto => dto.AffectedDetails);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventType, dto => dto.EventType);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventDetails, dto => dto.EventDetails);
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.Id), nameof(AuditEntryDto.Id));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.PerformingUserId), nameof(AuditEntryDto.PerformingUserId));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.PerformingDetails), nameof(AuditEntryDto.PerformingDetails));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.PerformingIp), nameof(AuditEntryDto.PerformingIp));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.EventDateUtc), nameof(AuditEntryDto.EventDateUtc));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.AffectedUserId), nameof(AuditEntryDto.AffectedUserId));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.AffectedDetails), nameof(AuditEntryDto.AffectedDetails));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.EventType), nameof(AuditEntryDto.EventType));
DefineMap<AuditEntry, AuditEntryDto>(nameof(AuditEntry.EventDetails), nameof(AuditEntryDto.EventDetails));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -8,17 +9,17 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IAuditItem))]
public sealed class AuditItemMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public AuditItemMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<AuditItem, LogDto>(src => src.Id, dto => dto.NodeId);
CacheMap<AuditItem, LogDto>(src => src.CreateDate, dto => dto.Datestamp);
CacheMap<AuditItem, LogDto>(src => src.UserId, dto => dto.UserId);
CacheMap<AuditItem, LogDto>(src => src.AuditType, dto => dto.Header);
CacheMap<AuditItem, LogDto>(src => src.Comment, dto => dto.Comment);
DefineMap<AuditItem, LogDto>(nameof(AuditItem.Id), nameof(LogDto.NodeId));
DefineMap<AuditItem, LogDto>(nameof(AuditItem.CreateDate), nameof(LogDto.Datestamp));
DefineMap<AuditItem, LogDto>(nameof(AuditItem.UserId), nameof(LogDto.UserId));
DefineMap<AuditItem, LogDto>(nameof(AuditItem.AuditType), nameof(LogDto.Header));
DefineMap<AuditItem, LogDto>(nameof(AuditItem.Comment), nameof(LogDto.Comment));
}
}
}
@@ -1,69 +1,81 @@
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using NPoco;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Persistence.Mappers
{
public abstract class BaseMapper : IDiscoverable
public abstract class BaseMapper
{
protected BaseMapper()
// note: using a Lazy<ISqlContext> here because during installs, we are resolving the
// mappers way before we have a configured IUmbracoDatabaseFactory, ie way before we
// have an ISqlContext - this is some nasty temporal coupling which we might want to
// cleanup eventually.
private readonly Lazy<ISqlContext> _sqlContext;
private readonly object _definedLock = new object();
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> _maps;
private ISqlSyntaxProvider _sqlSyntax;
private bool _defined;
protected BaseMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
{
Build();
_sqlContext = sqlContext;
_maps = maps;
}
internal abstract ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache { get; }
protected abstract void DefineMaps();
private void Build()
internal string Map(string propertyName)
{
BuildMap();
}
protected abstract void BuildMap();
internal string Map(ISqlSyntaxProvider sqlSyntax, string propertyName, bool throws = false)
{
if (PropertyInfoCache.TryGetValue(propertyName, out var dtoTypeProperty))
return GetColumnName(sqlSyntax, dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
if (throws)
throw new InvalidOperationException("Could not get the value with the key " + propertyName + " from the property info cache, keys available: " + string.Join(", ", PropertyInfoCache.Keys));
return string.Empty;
}
internal void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
{
var property = ResolveMapping(sourceMember, destinationMember);
PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property);
}
internal DtoMapModel ResolveMapping<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
{
var source = ExpressionHelper.FindProperty(sourceMember);
var destination = (PropertyInfo) ExpressionHelper.FindProperty(destinationMember).Item1;
if (destination == null)
lock (_definedLock)
{
throw new InvalidOperationException("The 'destination' returned was null, cannot resolve the mapping");
if (!_defined)
{
var sqlContext = _sqlContext.Value;
if (sqlContext == null)
throw new InvalidOperationException("Could not get an ISqlContext.");
_sqlSyntax = sqlContext.SqlSyntax;
DefineMaps();
_defined = true;
}
}
return new DtoMapModel(typeof(TDestination), destination, source.Item1.Name);
if (!_maps.TryGetValue(GetType(), out var mapperMaps))
throw new InvalidOperationException($"No maps defined for mapper {GetType().FullName}.");
if (!mapperMaps.TryGetValue(propertyName, out var mappedName))
throw new InvalidOperationException($"No map defined by mapper {GetType().FullName} for property {propertyName}.");
return mappedName;
}
internal virtual string GetColumnName(ISqlSyntaxProvider sqlSyntax, Type dtoType, PropertyInfo dtoProperty)
protected void DefineMap<TSource, TTarget>(string sourceName, string targetName)
{
var tableNameAttribute = dtoType.FirstAttribute<TableNameAttribute>();
if (_sqlSyntax == null)
throw new InvalidOperationException("Do not define maps outside of DefineMaps.");
var targetType = typeof(TTarget);
// TODO ensure that sourceName is a valid sourceType property (but, slow?)
var tableNameAttribute = targetType.FirstAttribute<TableNameAttribute>();
if (tableNameAttribute == null) throw new InvalidOperationException($"Type {targetType.FullName} is not marked with a TableName attribute.");
var tableName = tableNameAttribute.Value;
var columnAttribute = dtoProperty.FirstAttribute<ColumnAttribute>();
var columnName = columnAttribute.Name;
// TODO maybe get all properties once and then index them
var targetProperty = targetType.GetProperty(targetName);
if (targetProperty == null) throw new InvalidOperationException($"Type {targetType.FullName} does not have a property named {targetName}.");
var columnAttribute = targetProperty.FirstAttribute<ColumnAttribute>();
if (columnAttribute == null) throw new InvalidOperationException($"Property {targetType.FullName}.{targetName} is not marked with a Column attribute.");
var columnMap = sqlSyntax.GetQuotedTableName(tableName) + "." + sqlSyntax.GetQuotedColumnName(columnName);
return columnMap;
var columnName = columnAttribute.Name;
var columnMap = _sqlSyntax.GetQuotedTableName(tableName) + "." + _sqlSyntax.GetQuotedColumnName(columnName);
var mapperMaps = _maps.GetOrAdd(GetType(), type => new ConcurrentDictionary<string, string>());
mapperMaps[sourceName] = columnMap;
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -11,20 +12,20 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(Consent))]
public sealed class ConsentMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public ConsentMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<Consent, ConsentDto>(entity => entity.Id, dto => dto.Id);
CacheMap<Consent, ConsentDto>(entity => entity.Current, dto => dto.Current);
CacheMap<Consent, ConsentDto>(entity => entity.CreateDate, dto => dto.CreateDate);
CacheMap<Consent, ConsentDto>(entity => entity.Source, dto => dto.Source);
CacheMap<Consent, ConsentDto>(entity => entity.Context, dto => dto.Context);
CacheMap<Consent, ConsentDto>(entity => entity.Action, dto => dto.Action);
CacheMap<Consent, ConsentDto>(entity => entity.State, dto => dto.State);
CacheMap<Consent, ConsentDto>(entity => entity.Comment, dto => dto.Comment);
DefineMap<Consent, ConsentDto>(nameof(Consent.Id), nameof(ConsentDto.Id));
DefineMap<Consent, ConsentDto>(nameof(Consent.Current), nameof(ConsentDto.Current));
DefineMap<Consent, ConsentDto>(nameof(Consent.CreateDate), nameof(ConsentDto.CreateDate));
DefineMap<Consent, ConsentDto>(nameof(Consent.Source), nameof(ConsentDto.Source));
DefineMap<Consent, ConsentDto>(nameof(Consent.Context), nameof(ConsentDto.Context));
DefineMap<Consent, ConsentDto>(nameof(Consent.Action), nameof(ConsentDto.Action));
DefineMap<Consent, ConsentDto>(nameof(Consent.State), nameof(ConsentDto.State));
DefineMap<Consent, ConsentDto>(nameof(Consent.Comment), nameof(ConsentDto.Comment));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,36 +13,34 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IContent))]
public sealed class ContentMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public ContentMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
DefineMap<Content, NodeDto>(nameof(Content.Id), nameof(NodeDto.NodeId));
DefineMap<Content, NodeDto>(nameof(Content.Key), nameof(NodeDto.UniqueId));
CacheMap<Content, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<Content, NodeDto>(src => src.Key, dto => dto.UniqueId);
DefineMap<Content, ContentVersionDto>(nameof(Content.VersionId), nameof(ContentVersionDto.Id));
DefineMap<Content, ContentVersionDto>(nameof(Content.Name), nameof(ContentVersionDto.Text));
CacheMap<Content, ContentVersionDto>(src => src.VersionId, dto => dto.Id);
CacheMap<Content, ContentVersionDto>(src => src.Name, dto => dto.Text);
DefineMap<Content, NodeDto>(nameof(Content.ParentId), nameof(NodeDto.ParentId));
DefineMap<Content, NodeDto>(nameof(Content.Level), nameof(NodeDto.Level));
DefineMap<Content, NodeDto>(nameof(Content.Path), nameof(NodeDto.Path));
DefineMap<Content, NodeDto>(nameof(Content.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<Content, NodeDto>(nameof(Content.Trashed), nameof(NodeDto.Trashed));
CacheMap<Content, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<Content, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<Content, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<Content, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<Content, NodeDto>(src => src.Trashed, dto => dto.Trashed);
DefineMap<Content, NodeDto>(nameof(Content.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<Content, NodeDto>(nameof(Content.CreatorId), nameof(NodeDto.UserId));
DefineMap<Content, ContentDto>(nameof(Content.ContentTypeId), nameof(ContentDto.ContentTypeId));
CacheMap<Content, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<Content, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<Content, ContentDto>(src => src.ContentTypeId, dto => dto.ContentTypeId);
DefineMap<Content, ContentVersionDto>(nameof(Content.UpdateDate), nameof(ContentVersionDto.VersionDate));
DefineMap<Content, DocumentDto>(nameof(Content.Published), nameof(DocumentDto.Published));
CacheMap<Content, ContentVersionDto>(src => src.UpdateDate, dto => dto.VersionDate);
CacheMap<Content, DocumentDto>(src => src.Published, dto => dto.Published);
//CacheMap<Content, DocumentDto>(src => src.Name, dto => dto.Alias);
//DefineMap<Content, DocumentDto>(nameof(Content.Name), nameof(DocumentDto.Alias));
//CacheMap<Content, DocumentDto>(src => src, dto => dto.Newest);
//CacheMap<Content, DocumentDto>(src => src.Template, dto => dto.TemplateId);
//DefineMap<Content, DocumentDto>(nameof(Content.Template), nameof(DocumentDto.TemplateId));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,31 +13,29 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IContentType))]
public sealed class ContentTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public ContentTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
CacheMap<ContentType, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<ContentType, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<ContentType, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<ContentType, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<ContentType, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<ContentType, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<ContentType, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<ContentType, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<ContentType, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<ContentType, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<ContentType, ContentTypeDto>(src => src.Alias, dto => dto.Alias);
CacheMap<ContentType, ContentTypeDto>(src => src.AllowedAsRoot, dto => dto.AllowAtRoot);
CacheMap<ContentType, ContentTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<ContentType, ContentTypeDto>(src => src.Icon, dto => dto.Icon);
CacheMap<ContentType, ContentTypeDto>(src => src.IsContainer, dto => dto.IsContainer);
CacheMap<ContentType, ContentTypeDto>(src => src.IsElement, dto => dto.IsElement);
CacheMap<ContentType, ContentTypeDto>(src => src.Thumbnail, dto => dto.Thumbnail);
DefineMap<ContentType, NodeDto>(nameof(ContentType.Id), nameof(NodeDto.NodeId));
DefineMap<ContentType, NodeDto>(nameof(ContentType.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Level), nameof(NodeDto.Level));
DefineMap<ContentType, NodeDto>(nameof(ContentType.ParentId), nameof(NodeDto.ParentId));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Path), nameof(NodeDto.Path));
DefineMap<ContentType, NodeDto>(nameof(ContentType.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Name), nameof(NodeDto.Text));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Trashed), nameof(NodeDto.Trashed));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Key), nameof(NodeDto.UniqueId));
DefineMap<ContentType, NodeDto>(nameof(ContentType.CreatorId), nameof(NodeDto.UserId));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Alias), nameof(ContentTypeDto.Alias));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.AllowedAsRoot), nameof(ContentTypeDto.AllowAtRoot));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Description), nameof(ContentTypeDto.Description));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Icon), nameof(ContentTypeDto.Icon));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.IsContainer), nameof(ContentTypeDto.IsContainer));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.IsElement), nameof(ContentTypeDto.IsElement));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Thumbnail), nameof(ContentTypeDto.Thumbnail));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,25 +13,24 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IDataType))]
public sealed class DataTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public DataTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<DataType, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<DataType, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<DataType, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<DataType, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<DataType, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<DataType, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<DataType, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<DataType, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<DataType, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<DataType, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<DataType, DataTypeDto>(src => src.EditorAlias, dto => dto.EditorAlias);
CacheMap<DataType, DataTypeDto>(src => src.DatabaseType, dto => dto.DbType);
DefineMap<DataType, NodeDto>(nameof(DataType.Id), nameof(NodeDto.NodeId));
DefineMap<DataType, NodeDto>(nameof(DataType.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<DataType, NodeDto>(nameof(DataType.Level), nameof(NodeDto.Level));
DefineMap<DataType, NodeDto>(nameof(DataType.ParentId), nameof(NodeDto.ParentId));
DefineMap<DataType, NodeDto>(nameof(DataType.Path), nameof(NodeDto.Path));
DefineMap<DataType, NodeDto>(nameof(DataType.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<DataType, NodeDto>(nameof(DataType.Name), nameof(NodeDto.Text));
DefineMap<DataType, NodeDto>(nameof(DataType.Trashed), nameof(NodeDto.Trashed));
DefineMap<DataType, NodeDto>(nameof(DataType.Key), nameof(NodeDto.UniqueId));
DefineMap<DataType, NodeDto>(nameof(DataType.CreatorId), nameof(NodeDto.UserId));
DefineMap<DataType, DataTypeDto>(nameof(DataType.EditorAlias), nameof(DataTypeDto.EditorAlias));
DefineMap<DataType, DataTypeDto>(nameof(DataType.DatabaseType), nameof(DataTypeDto.DbType));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,16 +13,16 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IDictionaryItem))]
public sealed class DictionaryMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public DictionaryMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<DictionaryItem, DictionaryDto>(src => src.Id, dto => dto.PrimaryKey);
CacheMap<DictionaryItem, DictionaryDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<DictionaryItem, DictionaryDto>(src => src.ItemKey, dto => dto.Key);
CacheMap<DictionaryItem, DictionaryDto>(src => src.ParentId, dto => dto.Parent);
DefineMap<DictionaryItem, DictionaryDto>(nameof(DictionaryItem.Id), nameof(DictionaryDto.PrimaryKey));
DefineMap<DictionaryItem, DictionaryDto>(nameof(DictionaryItem.Key), nameof(DictionaryDto.UniqueId));
DefineMap<DictionaryItem, DictionaryDto>(nameof(DictionaryItem.ItemKey), nameof(DictionaryDto.Key));
DefineMap<DictionaryItem, DictionaryDto>(nameof(DictionaryItem.ParentId), nameof(DictionaryDto.Parent));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,16 +13,16 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IDictionaryTranslation))]
public sealed class DictionaryTranslationMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public DictionaryTranslationMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<DictionaryTranslation, LanguageTextDto>(src => src.Id, dto => dto.PrimaryKey);
CacheMap<DictionaryTranslation, LanguageTextDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<DictionaryTranslation, LanguageTextDto>(src => src.Language, dto => dto.LanguageId);
CacheMap<DictionaryTranslation, LanguageTextDto>(src => src.Value, dto => dto.Value);
DefineMap<DictionaryTranslation, LanguageTextDto>(nameof(DictionaryTranslation.Id), nameof(LanguageTextDto.PrimaryKey));
DefineMap<DictionaryTranslation, LanguageTextDto>(nameof(DictionaryTranslation.Key), nameof(LanguageTextDto.UniqueId));
DefineMap<DictionaryTranslation, LanguageTextDto>(nameof(DictionaryTranslation.Language), nameof(LanguageTextDto.LanguageId));
DefineMap<DictionaryTranslation, LanguageTextDto>(nameof(DictionaryTranslation.Value), nameof(LanguageTextDto.Value));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -8,16 +9,16 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(UmbracoDomain))]
public sealed class DomainMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public DomainMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<UmbracoDomain, DomainDto>(src => src.Id, dto => dto.Id);
CacheMap<UmbracoDomain, DomainDto>(src => src.RootContentId, dto => dto.RootStructureId);
CacheMap<UmbracoDomain, DomainDto>(src => src.LanguageId, dto => dto.DefaultLanguage);
CacheMap<UmbracoDomain, DomainDto>(src => src.DomainName, dto => dto.DomainName);
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.Id), nameof(DomainDto.Id));
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.RootContentId), nameof(DomainDto.RootStructureId));
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.LanguageId), nameof(DomainDto.DefaultLanguage));
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.DomainName), nameof(DomainDto.DomainName));
}
}
}
@@ -1,19 +0,0 @@
using System;
using System.Reflection;
namespace Umbraco.Core.Persistence.Mappers
{
internal class DtoMapModel
{
public DtoMapModel(Type type, PropertyInfo propertyInfo, string sourcePropertyName)
{
Type = type;
PropertyInfo = propertyInfo;
SourcePropertyName = sourcePropertyName;
}
public string SourcePropertyName { get; private set; }
public Type Type { get; private set; }
public PropertyInfo PropertyInfo { get; private set; }
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Persistence.Dtos;
@@ -8,25 +9,17 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IdentityUserLogin))]
public sealed class ExternalLoginMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public ExternalLoginMapper()
public ExternalLoginMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
protected override void DefineMaps()
{
BuildMap();
DefineMap<IdentityUserLogin, ExternalLoginDto>(nameof(IdentityUserLogin.Id), nameof(ExternalLoginDto.Id));
DefineMap<IdentityUserLogin, ExternalLoginDto>(nameof(IdentityUserLogin.CreateDate), nameof(ExternalLoginDto.CreateDate));
DefineMap<IdentityUserLogin, ExternalLoginDto>(nameof(IdentityUserLogin.LoginProvider), nameof(ExternalLoginDto.LoginProvider));
DefineMap<IdentityUserLogin, ExternalLoginDto>(nameof(IdentityUserLogin.ProviderKey), nameof(ExternalLoginDto.ProviderKey));
DefineMap<IdentityUserLogin, ExternalLoginDto>(nameof(IdentityUserLogin.UserId), nameof(ExternalLoginDto.UserId));
}
#region Overrides of BaseMapper
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
{
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.Id, dto => dto.Id);
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.LoginProvider, dto => dto.LoginProvider);
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.ProviderKey, dto => dto.ProviderKey);
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.UserId, dto => dto.UserId);
}
#endregion
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,15 +13,15 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(Language))]
public sealed class LanguageMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public LanguageMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<Language, LanguageDto>(src => src.Id, dto => dto.Id);
CacheMap<Language, LanguageDto>(src => src.IsoCode, dto => dto.IsoCode);
CacheMap<Language, LanguageDto>(src => src.CultureName, dto => dto.CultureName);
DefineMap<Language, LanguageDto>(nameof(Language.Id), nameof(LanguageDto.Id));
DefineMap<Language, LanguageDto>(nameof(Language.IsoCode), nameof(LanguageDto.IsoCode));
DefineMap<Language, LanguageDto>(nameof(Language.CultureName), nameof(LanguageDto.CultureName));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -8,22 +9,22 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IMacro))]
internal sealed class MacroMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public MacroMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<Macro, MacroDto>(src => src.Id, dto => dto.Id);
CacheMap<Macro, MacroDto>(src => src.Alias, dto => dto.Alias);
CacheMap<Macro, MacroDto>(src => src.CacheByPage, dto => dto.CacheByPage);
CacheMap<Macro, MacroDto>(src => src.CacheByMember, dto => dto.CachePersonalized);
CacheMap<Macro, MacroDto>(src => src.MacroType, dto => dto.MacroType);
CacheMap<Macro, MacroDto>(src => src.DontRender, dto => dto.DontRender);
CacheMap<Macro, MacroDto>(src => src.Name, dto => dto.Name);
CacheMap<Macro, MacroDto>(src => src.CacheDuration, dto => dto.RefreshRate);
CacheMap<Macro, MacroDto>(src => src.MacroSource, dto => dto.MacroSource);
CacheMap<Macro, MacroDto>(src => src.UseInEditor, dto => dto.UseInEditor);
DefineMap<Macro, MacroDto>(nameof(Macro.Id), nameof(MacroDto.Id));
DefineMap<Macro, MacroDto>(nameof(Macro.Alias), nameof(MacroDto.Alias));
DefineMap<Macro, MacroDto>(nameof(Macro.CacheByPage), nameof(MacroDto.CacheByPage));
DefineMap<Macro, MacroDto>(nameof(Macro.CacheByMember), nameof(MacroDto.CachePersonalized));
DefineMap<Macro, MacroDto>(nameof(Macro.MacroType), nameof(MacroDto.MacroType));
DefineMap<Macro, MacroDto>(nameof(Macro.DontRender), nameof(MacroDto.DontRender));
DefineMap<Macro, MacroDto>(nameof(Macro.Name), nameof(MacroDto.Name));
DefineMap<Macro, MacroDto>(nameof(Macro.CacheDuration), nameof(MacroDto.RefreshRate));
DefineMap<Macro, MacroDto>(nameof(Macro.MacroSource), nameof(MacroDto.MacroSource));
DefineMap<Macro, MacroDto>(nameof(Macro.UseInEditor), nameof(MacroDto.UseInEditor));
}
}
}
@@ -1,9 +1,14 @@
using Umbraco.Core.Composing;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Persistence.Mappers
{
public class MapperCollectionBuilder : LazyCollectionBuilderBase<MapperCollectionBuilder, MapperCollection, BaseMapper>
public class MapperCollectionBuilder : SetCollectionBuilderBase<MapperCollectionBuilder, MapperCollection, BaseMapper>
{
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> _maps
= new ConcurrentDictionary<Type, ConcurrentDictionary<string, string>>();
protected override MapperCollectionBuilder This => this;
public override void RegisterWith(IRegister register)
@@ -19,6 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers
register.Register<IMapperCollection>(factory => factory.GetInstance<MapperCollection>());
}
protected override BaseMapper CreateItem(IFactory factory, Type itemType)
{
return (BaseMapper) factory.CreateInstance(itemType, _maps);
}
public MapperCollectionBuilder AddCoreMappers()
{
Add<AccessMapper>();
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,29 +13,27 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(Umbraco.Core.Models.Media))]
public sealed class MediaMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public MediaMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.Id), nameof(NodeDto.NodeId));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.Key), nameof(NodeDto.UniqueId));
CacheMap<Models.Media, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<Models.Media, NodeDto>(src => src.Key, dto => dto.UniqueId);
DefineMap<Content, ContentVersionDto>(nameof(Content.VersionId), nameof(ContentVersionDto.Id));
CacheMap<Content, ContentVersionDto>(src => src.VersionId, dto => dto.Id);
CacheMap<Models.Media, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<Models.Media, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<Models.Media, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<Models.Media, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<Models.Media, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<Models.Media, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<Models.Media, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<Models.Media, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<Models.Media, ContentDto>(src => src.ContentTypeId, dto => dto.ContentTypeId);
CacheMap<Models.Media, ContentVersionDto>(src => src.UpdateDate, dto => dto.VersionDate);
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.Level), nameof(NodeDto.Level));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.ParentId), nameof(NodeDto.ParentId));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.Path), nameof(NodeDto.Path));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.Name), nameof(NodeDto.Text));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.Trashed), nameof(NodeDto.Trashed));
DefineMap<Models.Media, NodeDto>(nameof(Models.Media.CreatorId), nameof(NodeDto.UserId));
DefineMap<Models.Media, ContentDto>(nameof(Models.Media.ContentTypeId), nameof(ContentDto.ContentTypeId));
DefineMap<Models.Media, ContentVersionDto>(nameof(Models.Media.UpdateDate), nameof(ContentVersionDto.VersionDate));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,31 +13,29 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(MediaType))]
public sealed class MediaTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public MediaTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
CacheMap<MediaType, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<MediaType, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<MediaType, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<MediaType, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<MediaType, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<MediaType, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<MediaType, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<MediaType, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<MediaType, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<MediaType, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<MediaType, ContentTypeDto>(src => src.Alias, dto => dto.Alias);
CacheMap<MediaType, ContentTypeDto>(src => src.AllowedAsRoot, dto => dto.AllowAtRoot);
CacheMap<MediaType, ContentTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<MediaType, ContentTypeDto>(src => src.Icon, dto => dto.Icon);
CacheMap<MediaType, ContentTypeDto>(src => src.IsContainer, dto => dto.IsContainer);
CacheMap<MediaType, ContentTypeDto>(src => src.IsElement, dto => dto.IsElement);
CacheMap<MediaType, ContentTypeDto>(src => src.Thumbnail, dto => dto.Thumbnail);
DefineMap<MediaType, NodeDto>(nameof(MediaType.Id), nameof(NodeDto.NodeId));
DefineMap<MediaType, NodeDto>(nameof(MediaType.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<MediaType, NodeDto>(nameof(MediaType.Level), nameof(NodeDto.Level));
DefineMap<MediaType, NodeDto>(nameof(MediaType.ParentId), nameof(NodeDto.ParentId));
DefineMap<MediaType, NodeDto>(nameof(MediaType.Path), nameof(NodeDto.Path));
DefineMap<MediaType, NodeDto>(nameof(MediaType.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<MediaType, NodeDto>(nameof(MediaType.Name), nameof(NodeDto.Text));
DefineMap<MediaType, NodeDto>(nameof(MediaType.Trashed), nameof(NodeDto.Trashed));
DefineMap<MediaType, NodeDto>(nameof(MediaType.Key), nameof(NodeDto.UniqueId));
DefineMap<MediaType, NodeDto>(nameof(MediaType.CreatorId), nameof(NodeDto.UserId));
DefineMap<MediaType, ContentTypeDto>(nameof(MediaType.Alias), nameof(ContentTypeDto.Alias));
DefineMap<MediaType, ContentTypeDto>(nameof(MediaType.AllowedAsRoot), nameof(ContentTypeDto.AllowAtRoot));
DefineMap<MediaType, ContentTypeDto>(nameof(MediaType.Description), nameof(ContentTypeDto.Description));
DefineMap<MediaType, ContentTypeDto>(nameof(MediaType.Icon), nameof(ContentTypeDto.Icon));
DefineMap<MediaType, ContentTypeDto>(nameof(MediaType.IsContainer), nameof(ContentTypeDto.IsContainer));
DefineMap<MediaType, ContentTypeDto>(nameof(MediaType.IsElement), nameof(ContentTypeDto.IsElement));
DefineMap<MediaType, ContentTypeDto>(nameof(MediaType.Thumbnail), nameof(ContentTypeDto.Thumbnail));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -8,17 +9,17 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof (MemberGroup))]
public sealed class MemberGroupMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public MemberGroupMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<MemberGroup, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<MemberGroup, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<MemberGroup, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<MemberGroup, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<MemberGroup, NodeDto>(src => src.Key, dto => dto.UniqueId);
DefineMap<MemberGroup, NodeDto>(nameof(MemberGroup.Id), nameof(NodeDto.NodeId));
DefineMap<MemberGroup, NodeDto>(nameof(MemberGroup.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<MemberGroup, NodeDto>(nameof(MemberGroup.CreatorId), nameof(NodeDto.UserId));
DefineMap<MemberGroup, NodeDto>(nameof(MemberGroup.Name), nameof(NodeDto.Text));
DefineMap<MemberGroup, NodeDto>(nameof(MemberGroup.Key), nameof(NodeDto.UniqueId));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.Dtos;
@@ -13,47 +14,47 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(Member))]
public sealed class MemberMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public MemberMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<Member, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<Member, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<Member, NodeDto>(src => ((IUmbracoEntity)src).Level, dto => dto.Level);
CacheMap<Member, NodeDto>(src => ((IUmbracoEntity)src).ParentId, dto => dto.ParentId);
CacheMap<Member, NodeDto>(src => ((IUmbracoEntity)src).Path, dto => dto.Path);
CacheMap<Member, NodeDto>(src => ((IUmbracoEntity)src).SortOrder, dto => dto.SortOrder);
CacheMap<Member, NodeDto>(src => ((IUmbracoEntity)src).CreatorId, dto => dto.UserId);
CacheMap<Member, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<Member, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<Member, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<Member, ContentDto>(src => src.ContentTypeId, dto => dto.ContentTypeId);
CacheMap<Member, ContentTypeDto>(src => src.ContentTypeAlias, dto => dto.Alias);
CacheMap<Member, ContentVersionDto>(src => src.UpdateDate, dto => dto.VersionDate);
DefineMap<Member, NodeDto>(nameof(Member.Id), nameof(NodeDto.NodeId));
DefineMap<Member, NodeDto>(nameof(Member.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<Member, NodeDto>(nameof(Member.Level), nameof(NodeDto.Level));
DefineMap<Member, NodeDto>(nameof(Member.ParentId), nameof(NodeDto.ParentId));
DefineMap<Member, NodeDto>(nameof(Member.Path), nameof(NodeDto.Path));
DefineMap<Member, NodeDto>(nameof(Member.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<Member, NodeDto>(nameof(Member.CreatorId), nameof(NodeDto.UserId));
DefineMap<Member, NodeDto>(nameof(Member.Name), nameof(NodeDto.Text));
DefineMap<Member, NodeDto>(nameof(Member.Trashed), nameof(NodeDto.Trashed));
DefineMap<Member, NodeDto>(nameof(Member.Key), nameof(NodeDto.UniqueId));
DefineMap<Member, ContentDto>(nameof(Member.ContentTypeId), nameof(ContentDto.ContentTypeId));
DefineMap<Member, ContentTypeDto>(nameof(Member.ContentTypeAlias), nameof(ContentTypeDto.Alias));
DefineMap<Member, ContentVersionDto>(nameof(Member.UpdateDate), nameof(ContentVersionDto.VersionDate));
CacheMap<Member, MemberDto>(src => src.Email, dto => dto.Email);
CacheMap<Member, MemberDto>(src => src.Username, dto => dto.LoginName);
CacheMap<Member, MemberDto>(src => src.RawPasswordValue, dto => dto.Password);
DefineMap<Member, MemberDto>(nameof(Member.Email), nameof(MemberDto.Email));
DefineMap<Member, MemberDto>(nameof(Member.Username), nameof(MemberDto.LoginName));
DefineMap<Member, MemberDto>(nameof(Member.RawPasswordValue), nameof(MemberDto.Password));
CacheMap<Member, PropertyDataDto>(src => src.IsApproved, dto => dto.IntegerValue);
CacheMap<Member, PropertyDataDto>(src => src.IsLockedOut, dto => dto.IntegerValue);
CacheMap<Member, PropertyDataDto>(src => src.Comments, dto => dto.TextValue);
CacheMap<Member, PropertyDataDto>(src => src.RawPasswordAnswerValue, dto => dto.VarcharValue);
CacheMap<Member, PropertyDataDto>(src => src.PasswordQuestion, dto => dto.VarcharValue);
CacheMap<Member, PropertyDataDto>(src => src.FailedPasswordAttempts, dto => dto.IntegerValue);
CacheMap<Member, PropertyDataDto>(src => src.LastLockoutDate, dto => dto.DateValue);
CacheMap<Member, PropertyDataDto>(src => src.LastLoginDate, dto => dto.DateValue);
CacheMap<Member, PropertyDataDto>(src => src.LastPasswordChangeDate, dto => dto.DateValue);
DefineMap<Member, PropertyDataDto>(nameof(Member.IsApproved), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.IsLockedOut), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.Comments), nameof(PropertyDataDto.TextValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.RawPasswordAnswerValue), nameof(PropertyDataDto.VarcharValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.PasswordQuestion), nameof(PropertyDataDto.VarcharValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.FailedPasswordAttempts), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.LastLockoutDate), nameof(PropertyDataDto.DateValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.LastLoginDate), nameof(PropertyDataDto.DateValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.LastPasswordChangeDate), nameof(PropertyDataDto.DateValue));
/* Internal experiment */
CacheMap<Member, PropertyDataDto>(src => src.DateTimePropertyValue, dto => dto.DateValue);
CacheMap<Member, PropertyDataDto>(src => src.IntegerPropertyValue, dto => dto.IntegerValue);
CacheMap<Member, PropertyDataDto>(src => src.BoolPropertyValue, dto => dto.IntegerValue);
CacheMap<Member, PropertyDataDto>(src => src.LongStringPropertyValue, dto => dto.TextValue);
CacheMap<Member, PropertyDataDto>(src => src.ShortStringPropertyValue, dto => dto.VarcharValue);
CacheMap<Member, PropertyTypeDto>(src => src.PropertyTypeAlias, dto => dto.Alias);
DefineMap<Member, PropertyDataDto>(nameof(Member.DateTimePropertyValue), nameof(PropertyDataDto.DateValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.IntegerPropertyValue), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.BoolPropertyValue), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.LongStringPropertyValue), nameof(PropertyDataDto.TextValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.ShortStringPropertyValue), nameof(PropertyDataDto.VarcharValue));
DefineMap<Member, PropertyTypeDto>(nameof(Member.PropertyTypeAlias), nameof(PropertyTypeDto.Alias));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,31 +13,29 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof (IMemberType))]
public sealed class MemberTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public MemberTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
CacheMap<MemberType, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<MemberType, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<MemberType, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<MemberType, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<MemberType, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<MemberType, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<MemberType, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<MemberType, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<MemberType, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<MemberType, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<MemberType, ContentTypeDto>(src => src.Alias, dto => dto.Alias);
CacheMap<MemberType, ContentTypeDto>(src => src.AllowedAsRoot, dto => dto.AllowAtRoot);
CacheMap<MemberType, ContentTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<MemberType, ContentTypeDto>(src => src.Icon, dto => dto.Icon);
CacheMap<MemberType, ContentTypeDto>(src => src.IsContainer, dto => dto.IsContainer);
CacheMap<MemberType, ContentTypeDto>(src => src.IsElement, dto => dto.IsElement);
CacheMap<MemberType, ContentTypeDto>(src => src.Thumbnail, dto => dto.Thumbnail);
DefineMap<MemberType, NodeDto>(nameof(MemberType.Id), nameof(NodeDto.NodeId));
DefineMap<MemberType, NodeDto>(nameof(MemberType.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<MemberType, NodeDto>(nameof(MemberType.Level), nameof(NodeDto.Level));
DefineMap<MemberType, NodeDto>(nameof(MemberType.ParentId), nameof(NodeDto.ParentId));
DefineMap<MemberType, NodeDto>(nameof(MemberType.Path), nameof(NodeDto.Path));
DefineMap<MemberType, NodeDto>(nameof(MemberType.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<MemberType, NodeDto>(nameof(MemberType.Name), nameof(NodeDto.Text));
DefineMap<MemberType, NodeDto>(nameof(MemberType.Trashed), nameof(NodeDto.Trashed));
DefineMap<MemberType, NodeDto>(nameof(MemberType.Key), nameof(NodeDto.UniqueId));
DefineMap<MemberType, NodeDto>(nameof(MemberType.CreatorId), nameof(NodeDto.UserId));
DefineMap<MemberType, ContentTypeDto>(nameof(MemberType.Alias), nameof(ContentTypeDto.Alias));
DefineMap<MemberType, ContentTypeDto>(nameof(MemberType.AllowedAsRoot), nameof(ContentTypeDto.AllowAtRoot));
DefineMap<MemberType, ContentTypeDto>(nameof(MemberType.Description), nameof(ContentTypeDto.Description));
DefineMap<MemberType, ContentTypeDto>(nameof(MemberType.Icon), nameof(ContentTypeDto.Icon));
DefineMap<MemberType, ContentTypeDto>(nameof(MemberType.IsContainer), nameof(ContentTypeDto.IsContainer));
DefineMap<MemberType, ContentTypeDto>(nameof(MemberType.IsElement), nameof(ContentTypeDto.IsElement));
DefineMap<MemberType, ContentTypeDto>(nameof(MemberType.Thumbnail), nameof(ContentTypeDto.Thumbnail));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -11,16 +12,16 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(PropertyGroup))]
public sealed class PropertyGroupMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public PropertyGroupMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<PropertyGroup, PropertyTypeGroupDto>(src => src.Id, dto => dto.Id);
CacheMap<PropertyGroup, PropertyTypeGroupDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<PropertyGroup, PropertyTypeGroupDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<PropertyGroup, PropertyTypeGroupDto>(src => src.Name, dto => dto.Text);
DefineMap<PropertyGroup, PropertyTypeGroupDto>(nameof(PropertyGroup.Id), nameof(PropertyTypeGroupDto.Id));
DefineMap<PropertyGroup, PropertyTypeGroupDto>(nameof(PropertyGroup.Key), nameof(PropertyTypeGroupDto.UniqueId));
DefineMap<PropertyGroup, PropertyTypeGroupDto>(nameof(PropertyGroup.SortOrder), nameof(PropertyTypeGroupDto.SortOrder));
DefineMap<PropertyGroup, PropertyTypeGroupDto>(nameof(PropertyGroup.Name), nameof(PropertyTypeGroupDto.Text));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -7,14 +8,14 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(Property))]
public sealed class PropertyMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public PropertyMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<Property, PropertyDataDto>(src => src.Id, dto => dto.Id);
CacheMap<Property, PropertyDataDto>(src => src.PropertyTypeId, dto => dto.PropertyTypeId);
DefineMap<Property, PropertyDataDto>(nameof(Property.Id), nameof(PropertyDataDto.Id));
DefineMap<Property, PropertyDataDto>(nameof(Property.PropertyTypeId), nameof(PropertyDataDto.PropertyTypeId));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -11,25 +12,23 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(PropertyType))]
public sealed class PropertyTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public PropertyTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
CacheMap<PropertyType, PropertyTypeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<PropertyType, PropertyTypeDto>(src => src.Id, dto => dto.Id);
CacheMap<PropertyType, PropertyTypeDto>(src => src.Alias, dto => dto.Alias);
CacheMap<PropertyType, PropertyTypeDto>(src => src.DataTypeId, dto => dto.DataTypeId);
CacheMap<PropertyType, PropertyTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<PropertyType, PropertyTypeDto>(src => src.Mandatory, dto => dto.Mandatory);
CacheMap<PropertyType, PropertyTypeDto>(src => src.Name, dto => dto.Name);
CacheMap<PropertyType, PropertyTypeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<PropertyType, PropertyTypeDto>(src => src.ValidationRegExp, dto => dto.ValidationRegExp);
CacheMap<PropertyType, DataTypeDto>(src => src.PropertyEditorAlias, dto => dto.EditorAlias);
CacheMap<PropertyType, DataTypeDto>(src => src.ValueStorageType, dto => dto.DbType);
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Key), nameof(PropertyTypeDto.UniqueId));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Id), nameof(PropertyTypeDto.Id));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Alias), nameof(PropertyTypeDto.Alias));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.DataTypeId), nameof(PropertyTypeDto.DataTypeId));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Description), nameof(PropertyTypeDto.Description));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Mandatory), nameof(PropertyTypeDto.Mandatory));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Name), nameof(PropertyTypeDto.Name));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.SortOrder), nameof(PropertyTypeDto.SortOrder));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.ValidationRegExp), nameof(PropertyTypeDto.ValidationRegExp));
DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.PropertyEditorAlias), nameof(DataTypeDto.EditorAlias));
DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.ValueStorageType), nameof(DataTypeDto.DbType));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,18 +13,18 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(Relation))]
public sealed class RelationMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public RelationMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<Relation, RelationDto>(src => src.Id, dto => dto.Id);
CacheMap<Relation, RelationDto>(src => src.ChildId, dto => dto.ChildId);
CacheMap<Relation, RelationDto>(src => src.Comment, dto => dto.Comment);
CacheMap<Relation, RelationDto>(src => src.CreateDate, dto => dto.Datetime);
CacheMap<Relation, RelationDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<Relation, RelationDto>(src => src.RelationTypeId, dto => dto.RelationType);
DefineMap<Relation, RelationDto>(nameof(Relation.Id), nameof(RelationDto.Id));
DefineMap<Relation, RelationDto>(nameof(Relation.ChildId), nameof(RelationDto.ChildId));
DefineMap<Relation, RelationDto>(nameof(Relation.Comment), nameof(RelationDto.Comment));
DefineMap<Relation, RelationDto>(nameof(Relation.CreateDate), nameof(RelationDto.Datetime));
DefineMap<Relation, RelationDto>(nameof(Relation.ParentId), nameof(RelationDto.ParentId));
DefineMap<Relation, RelationDto>(nameof(Relation.RelationTypeId), nameof(RelationDto.RelationType));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,18 +13,18 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IRelationType))]
public sealed class RelationTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public RelationTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<RelationType, RelationTypeDto>(src => src.Id, dto => dto.Id);
CacheMap<RelationType, RelationTypeDto>(src => src.Alias, dto => dto.Alias);
CacheMap<RelationType, RelationTypeDto>(src => src.ChildObjectType, dto => dto.ChildObjectType);
CacheMap<RelationType, RelationTypeDto>(src => src.IsBidirectional, dto => dto.Dual);
CacheMap<RelationType, RelationTypeDto>(src => src.Name, dto => dto.Name);
CacheMap<RelationType, RelationTypeDto>(src => src.ParentObjectType, dto => dto.ParentObjectType);
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.Id), nameof(RelationTypeDto.Id));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.Alias), nameof(RelationTypeDto.Alias));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.ChildObjectType), nameof(RelationTypeDto.ChildObjectType));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.IsBidirectional), nameof(RelationTypeDto.Dual));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.Name), nameof(RelationTypeDto.Name));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.ParentObjectType), nameof(RelationTypeDto.ParentObjectType));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -8,19 +9,19 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(IServerRegistration))]
internal sealed class ServerRegistrationMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public ServerRegistrationMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.Id, dto => dto.Id);
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.IsActive, dto => dto.IsActive);
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.IsMaster, dto => dto.IsMaster);
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.ServerAddress, dto => dto.ServerAddress);
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.CreateDate, dto => dto.DateRegistered);
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.UpdateDate, dto => dto.DateAccessed);
CacheMap<ServerRegistration, ServerRegistrationDto>(src => src.ServerIdentity, dto => dto.ServerIdentity);
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.Id), nameof(ServerRegistrationDto.Id));
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.IsActive), nameof(ServerRegistrationDto.IsActive));
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.IsMaster), nameof(ServerRegistrationDto.IsMaster));
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.ServerAddress), nameof(ServerRegistrationDto.ServerAddress));
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.CreateDate), nameof(ServerRegistrationDto.DateRegistered));
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.UpdateDate), nameof(ServerRegistrationDto.DateAccessed));
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.ServerIdentity), nameof(ServerRegistrationDto.ServerIdentity));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -8,31 +9,29 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(SimpleContentType))]
public sealed class SimpleContentTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public SimpleContentTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
CacheMap<ContentType, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<ContentType, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<ContentType, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<ContentType, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<ContentType, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<ContentType, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<ContentType, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<ContentType, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<ContentType, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<ContentType, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<ContentType, ContentTypeDto>(src => src.Alias, dto => dto.Alias);
CacheMap<ContentType, ContentTypeDto>(src => src.AllowedAsRoot, dto => dto.AllowAtRoot);
CacheMap<ContentType, ContentTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<ContentType, ContentTypeDto>(src => src.Icon, dto => dto.Icon);
CacheMap<ContentType, ContentTypeDto>(src => src.IsContainer, dto => dto.IsContainer);
CacheMap<ContentType, ContentTypeDto>(src => src.IsElement, dto => dto.IsElement);
CacheMap<ContentType, ContentTypeDto>(src => src.Thumbnail, dto => dto.Thumbnail);
DefineMap<ContentType, NodeDto>(nameof(ContentType.Id), nameof(NodeDto.NodeId));
DefineMap<ContentType, NodeDto>(nameof(ContentType.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Level), nameof(NodeDto.Level));
DefineMap<ContentType, NodeDto>(nameof(ContentType.ParentId), nameof(NodeDto.ParentId));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Path), nameof(NodeDto.Path));
DefineMap<ContentType, NodeDto>(nameof(ContentType.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Name), nameof(NodeDto.Text));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Trashed), nameof(NodeDto.Trashed));
DefineMap<ContentType, NodeDto>(nameof(ContentType.Key), nameof(NodeDto.UniqueId));
DefineMap<ContentType, NodeDto>(nameof(ContentType.CreatorId), nameof(NodeDto.UserId));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Alias), nameof(ContentTypeDto.Alias));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.AllowedAsRoot), nameof(ContentTypeDto.AllowAtRoot));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Description), nameof(ContentTypeDto.Description));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Icon), nameof(ContentTypeDto.Icon));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.IsContainer), nameof(ContentTypeDto.IsContainer));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.IsElement), nameof(ContentTypeDto.IsElement));
DefineMap<ContentType, ContentTypeDto>(nameof(ContentType.Thumbnail), nameof(ContentTypeDto.Thumbnail));
}
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,18 +13,16 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(ITag))]
public sealed class TagMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public TagMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
CacheMap<Tag, TagDto>(src => src.Id, dto => dto.Id);
CacheMap<Tag, TagDto>(src => src.Text, dto => dto.Text);
CacheMap<Tag, TagDto>(src => src.Group, dto => dto.Group);
CacheMap<Tag, TagDto>(src => src.LanguageId, dto => dto.LanguageId);
DefineMap<Tag, TagDto>(nameof(Tag.Id), nameof(TagDto.Id));
DefineMap<Tag, TagDto>(nameof(Tag.Text), nameof(TagDto.Text));
DefineMap<Tag, TagDto>(nameof(Tag.Group), nameof(TagDto.Group));
DefineMap<Tag, TagDto>(nameof(Tag.LanguageId), nameof(TagDto.LanguageId));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -12,18 +13,16 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(ITemplate))]
public sealed class TemplateMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public TemplateMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
if (PropertyInfoCache.IsEmpty == false) return;
CacheMap<Template, TemplateDto>(src => src.Id, dto => dto.NodeId);
CacheMap<Template, NodeDto>(src => src.MasterTemplateId, dto => dto.ParentId);
CacheMap<Template, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<Template, TemplateDto>(src => src.Alias, dto => dto.Alias);
DefineMap<Template, TemplateDto>(nameof(Template.Id), nameof(TemplateDto.NodeId));
DefineMap<Template, NodeDto>(nameof(Template.MasterTemplateId), nameof(NodeDto.ParentId));
DefineMap<Template, NodeDto>(nameof(Template.Key), nameof(NodeDto.UniqueId));
DefineMap<Template, TemplateDto>(nameof(Template.Alias), nameof(TemplateDto.Alias));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.Dtos;
@@ -7,22 +8,22 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof (IUmbracoEntity))]
public sealed class UmbracoEntityMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public UmbracoEntityMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<IUmbracoEntity, NodeDto>(src => src.Id, dto => dto.NodeId);
CacheMap<IUmbracoEntity, NodeDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<IUmbracoEntity, NodeDto>(src => src.Level, dto => dto.Level);
CacheMap<IUmbracoEntity, NodeDto>(src => src.ParentId, dto => dto.ParentId);
CacheMap<IUmbracoEntity, NodeDto>(src => src.Path, dto => dto.Path);
CacheMap<IUmbracoEntity, NodeDto>(src => src.SortOrder, dto => dto.SortOrder);
CacheMap<IUmbracoEntity, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<IUmbracoEntity, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<IUmbracoEntity, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<IUmbracoEntity, NodeDto>(src => src.CreatorId, dto => dto.UserId);
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.Id), nameof(NodeDto.NodeId));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.CreateDate), nameof(NodeDto.CreateDate));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.Level), nameof(NodeDto.Level));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.ParentId), nameof(NodeDto.ParentId));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.Path), nameof(NodeDto.Path));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.SortOrder), nameof(NodeDto.SortOrder));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.Name), nameof(NodeDto.Text));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.Trashed), nameof(NodeDto.Trashed));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.Key), nameof(NodeDto.UniqueId));
DefineMap<IUmbracoEntity, NodeDto>(nameof(IUmbracoEntity.CreatorId), nameof(NodeDto.UserId));
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.Dtos;
@@ -12,29 +13,18 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(UserGroup))]
public sealed class UserGroupMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public UserGroupMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
// otherwise that would fail because there is no public constructor.
public UserGroupMapper()
protected override void DefineMaps()
{
BuildMap();
DefineMap<UserGroup, UserGroupDto>(nameof(UserGroup.Id), nameof(UserGroupDto.Id));
DefineMap<UserGroup, UserGroupDto>(nameof(UserGroup.Alias), nameof(UserGroupDto.Alias));
DefineMap<UserGroup, UserGroupDto>(nameof(UserGroup.Name), nameof(UserGroupDto.Name));
DefineMap<UserGroup, UserGroupDto>(nameof(UserGroup.Icon), nameof(UserGroupDto.Icon));
DefineMap<UserGroup, UserGroupDto>(nameof(UserGroup.StartContentId), nameof(UserGroupDto.StartContentId));
DefineMap<UserGroup, UserGroupDto>(nameof(UserGroup.StartMediaId), nameof(UserGroupDto.StartMediaId));
}
#region Overrides of BaseMapper
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
{
CacheMap<UserGroup, UserGroupDto>(src => src.Id, dto => dto.Id);
CacheMap<UserGroup, UserGroupDto>(src => src.Alias, dto => dto.Alias);
CacheMap<UserGroup, UserGroupDto>(src => src.Name, dto => dto.Name);
CacheMap<UserGroup, UserGroupDto>(src => src.Icon, dto => dto.Icon);
CacheMap<UserGroup, UserGroupDto>(src => src.StartContentId, dto => dto.StartContentId);
CacheMap<UserGroup, UserGroupDto>(src => src.StartMediaId, dto => dto.StartMediaId);
}
#endregion
}
}
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.Dtos;
@@ -8,28 +9,28 @@ namespace Umbraco.Core.Persistence.Mappers
[MapperFor(typeof(User))]
public sealed class UserMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public UserMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
: base(sqlContext, maps)
{ }
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
protected override void BuildMap()
protected override void DefineMaps()
{
CacheMap<User, UserDto>(src => src.Id, dto => dto.Id);
CacheMap<User, UserDto>(src => src.Email, dto => dto.Email);
CacheMap<User, UserDto>(src => src.Username, dto => dto.Login);
CacheMap<User, UserDto>(src => src.RawPasswordValue, dto => dto.Password);
CacheMap<User, UserDto>(src => src.Name, dto => dto.UserName);
DefineMap<User, UserDto>(nameof(User.Id), nameof(UserDto.Id));
DefineMap<User, UserDto>(nameof(User.Email), nameof(UserDto.Email));
DefineMap<User, UserDto>(nameof(User.Username), nameof(UserDto.Login));
DefineMap<User, UserDto>(nameof(User.RawPasswordValue), nameof(UserDto.Password));
DefineMap<User, UserDto>(nameof(User.Name), nameof(UserDto.UserName));
//NOTE: This column in the db is *not* used!
//CacheMap<User, UserDto>(src => src.DefaultPermissions, dto => dto.DefaultPermissions);
CacheMap<User, UserDto>(src => src.IsApproved, dto => dto.Disabled);
CacheMap<User, UserDto>(src => src.IsLockedOut, dto => dto.NoConsole);
CacheMap<User, UserDto>(src => src.Language, dto => dto.UserLanguage);
CacheMap<User, UserDto>(src => src.CreateDate, dto => dto.CreateDate);
CacheMap<User, UserDto>(src => src.UpdateDate, dto => dto.UpdateDate);
CacheMap<User, UserDto>(src => src.LastLockoutDate, dto => dto.LastLockoutDate);
CacheMap<User, UserDto>(src => src.LastLoginDate, dto => dto.LastLoginDate);
CacheMap<User, UserDto>(src => src.LastPasswordChangeDate, dto => dto.LastPasswordChangeDate);
CacheMap<User, UserDto>(src => src.SecurityStamp, dto => dto.SecurityStampToken);
//DefineMap<User, UserDto>(nameof(User.DefaultPermissions), nameof(UserDto.DefaultPermissions));
DefineMap<User, UserDto>(nameof(User.IsApproved), nameof(UserDto.Disabled));
DefineMap<User, UserDto>(nameof(User.IsLockedOut), nameof(UserDto.NoConsole));
DefineMap<User, UserDto>(nameof(User.Language), nameof(UserDto.UserLanguage));
DefineMap<User, UserDto>(nameof(User.CreateDate), nameof(UserDto.CreateDate));
DefineMap<User, UserDto>(nameof(User.UpdateDate), nameof(UserDto.UpdateDate));
DefineMap<User, UserDto>(nameof(User.LastLockoutDate), nameof(UserDto.LastLockoutDate));
DefineMap<User, UserDto>(nameof(User.LastLoginDate), nameof(UserDto.LastLoginDate));
DefineMap<User, UserDto>(nameof(User.LastPasswordChangeDate), nameof(UserDto.LastPasswordChangeDate));
DefineMap<User, UserDto>(nameof(User.SecurityStamp), nameof(UserDto.SecurityStampToken));
}
}
}
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Querying
//don't execute if compiled
if (Visited == false)
{
var field = _mapper.Map(SqlSyntax, m.Member.Name, true);
var field = _mapper.Map(m.Member.Name);
if (field.IsNullOrWhiteSpace())
throw new InvalidOperationException($"The mapper returned an empty field for the member name: {m.Member.Name} for type: {m.Expression.Type}.");
return field;
@@ -48,7 +48,7 @@ namespace Umbraco.Core.Persistence.Querying
//don't execute if compiled
if (Visited == false)
{
var field = _mapper.Map(SqlSyntax, m.Member.Name, true);
var field = _mapper.Map(m.Member.Name);
if (field.IsNullOrWhiteSpace())
throw new InvalidOperationException($"The mapper returned an empty field for the member name: {m.Member.Name} for type: {m.Expression.Type}.");
return field;
@@ -70,7 +70,7 @@ namespace Umbraco.Core.Persistence.Querying
if (Visited == false)
{
var subMapper = _mappers[m.Expression.Type]; // throws if not found
var field = subMapper.Map(SqlSyntax, m.Member.Name, true);
var field = subMapper.Map(m.Member.Name);
if (field.IsNullOrWhiteSpace())
throw new InvalidOperationException($"The mapper returned an empty field for the member name: {m.Member.Name} for type: {m.Expression.Type}");
return field;
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
// TODO
// this should be IContentTypeRepository, and what is IContentTypeRepository at the moment should
// become IDocumentTypeRepository - but since these interfaces are public, that would be breaking
/// <summary>
/// Represents the content types common repository, dealing with document, media and member types.
/// </summary>
public interface IContentTypeCommonRepository
{
/// <summary>
/// Gets and cache all types.
/// </summary>
IEnumerable<IContentTypeComposition> GetAllTypes();
/// <summary>
/// Clears the cache.
/// </summary>
void ClearCache();
}
}
@@ -1,16 +1,8 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IMediaTypeRepository : IContentTypeRepositoryBase<IMediaType>
{
/// <summary>
/// Gets all entities of the specified <see cref="PropertyType"/> query
/// </summary>
/// <param name="query"></param>
/// <returns>An enumerable list of <see cref="IContentType"/> objects</returns>
IEnumerable<IMediaType> GetByQuery(IQuery<PropertyType> query);
}
{ }
}
@@ -0,0 +1,285 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
/// <summary>
/// Implements <see cref="IContentTypeCommonRepository"/>.
/// </summary>
internal class ContentTypeCommonRepository : IContentTypeCommonRepository
{
private const string CacheKey = "Umbraco.Core.Persistence.Repositories.Implement.ContentTypeCommonRepository::AllTypes";
private readonly AppCaches _appCaches;
private readonly IScopeAccessor _scopeAccessor;
private readonly ITemplateRepository _templateRepository;
/// <summary>
/// Initializes a new instance of the <see cref="IContentTypeCommonRepository"/> class.
/// </summary>
public ContentTypeCommonRepository(IScopeAccessor scopeAccessor, ITemplateRepository templateRepository, AppCaches appCaches)
{
_scopeAccessor = scopeAccessor;
_templateRepository = templateRepository;
_appCaches = appCaches;
}
private IScope AmbientScope => _scopeAccessor.AmbientScope;
private IUmbracoDatabase Database => AmbientScope.Database;
private ISqlContext SqlContext => AmbientScope.SqlContext;
private Sql<ISqlContext> Sql() => SqlContext.Sql();
//private Sql<ISqlContext> Sql(string sql, params object[] args) => SqlContext.Sql(sql, args);
//private ISqlSyntaxProvider SqlSyntax => SqlContext.SqlSyntax;
//private IQuery<T> Query<T>() => SqlContext.Query<T>();
/// <inheritdoc />
public IEnumerable<IContentTypeComposition> GetAllTypes()
{
// use a 5 minutes sliding cache - same as FullDataSet cache policy
return _appCaches.RuntimeCache.GetCacheItem(CacheKey, GetAllTypesInternal, TimeSpan.FromMinutes(5), true);
}
/// <inheritdoc />
public void ClearCache()
{
_appCaches.RuntimeCache.Clear(CacheKey);
}
private IEnumerable<IContentTypeComposition> GetAllTypesInternal()
{
var contentTypes = new Dictionary<int, IContentTypeComposition>();
// get content types
var sql1 = Sql()
.Select<ContentTypeDto>(r => r.Select(x => x.NodeDto))
.From<ContentTypeDto>()
.InnerJoin<NodeDto>().On<ContentTypeDto, NodeDto>((ct, n) => ct.NodeId == n.NodeId)
.OrderBy<ContentTypeDto>(x => x.NodeId);
var contentTypeDtos = Database.Fetch<ContentTypeDto>(sql1);
// get allowed content types
var sql2 = Sql()
.Select<ContentTypeAllowedContentTypeDto>()
.From<ContentTypeAllowedContentTypeDto>()
.OrderBy<ContentTypeAllowedContentTypeDto>(x => x.Id);
var allowedDtos = Database.Fetch<ContentTypeAllowedContentTypeDto>(sql2);
// prepare
// note: same alias could be used for media, content... but always different ids = ok
var aliases = contentTypeDtos.ToDictionary(x => x.NodeId, x => x.Alias);
// create
var allowedDtoIx = 0;
foreach (var contentTypeDto in contentTypeDtos)
{
// create content type
IContentTypeComposition contentType;
if (contentTypeDto.NodeDto.NodeObjectType == Constants.ObjectTypes.MediaType)
contentType = ContentTypeFactory.BuildMediaTypeEntity(contentTypeDto);
else if (contentTypeDto.NodeDto.NodeObjectType == Constants.ObjectTypes.DocumentType)
contentType = ContentTypeFactory.BuildContentTypeEntity(contentTypeDto);
else if (contentTypeDto.NodeDto.NodeObjectType == Constants.ObjectTypes.MemberType)
contentType = ContentTypeFactory.BuildMemberTypeEntity(contentTypeDto);
else throw new Exception("panic");
contentTypes.Add(contentType.Id, contentType);
// map allowed content types
var allowedContentTypes = new List<ContentTypeSort>();
while (allowedDtoIx < allowedDtos.Count && allowedDtos[allowedDtoIx].Id == contentTypeDto.NodeId)
{
var allowedDto = allowedDtos[allowedDtoIx];
if (!aliases.TryGetValue(allowedDto.AllowedId, out var alias)) continue;
allowedContentTypes.Add(new ContentTypeSort(new Lazy<int>(() => allowedDto.AllowedId), allowedDto.SortOrder, alias));
allowedDtoIx++;
}
contentType.AllowedContentTypes = allowedContentTypes;
}
MapTemplates(contentTypes);
MapComposition(contentTypes);
MapGroupsAndProperties(contentTypes);
// finalize
foreach (var contentType in contentTypes.Values)
{
contentType.ResetDirtyProperties(false);
}
return contentTypes.Values;
}
private void MapTemplates(Dictionary<int, IContentTypeComposition> contentTypes)
{
// get templates
var sql1 = Sql()
.Select<ContentTypeTemplateDto>()
.From<ContentTypeTemplateDto>()
.OrderBy<ContentTypeTemplateDto>(x => x.ContentTypeNodeId);
var templateDtos = Database.Fetch<ContentTypeTemplateDto>(sql1);
//var templates = templateRepository.GetMany(templateDtos.Select(x => x.TemplateNodeId).ToArray()).ToDictionary(x => x.Id, x => x);
var templates = _templateRepository.GetMany().ToDictionary(x => x.Id, x => x);
var templateDtoIx = 0;
foreach (var c in contentTypes.Values)
{
if (!(c is ContentType contentType)) continue;
// map allowed templates
var allowedTemplates = new List<ITemplate>();
var defaultTemplateId = 0;
while (templateDtoIx < templateDtos.Count && templateDtos[templateDtoIx].ContentTypeNodeId == contentType.Id)
{
var allowedDto = templateDtos[templateDtoIx];
if (!templates.TryGetValue(allowedDto.TemplateNodeId, out var template)) continue;
allowedTemplates.Add(template);
templateDtoIx++;
if (allowedDto.IsDefault)
defaultTemplateId = template.Id;
}
contentType.AllowedTemplates = allowedTemplates;
contentType.DefaultTemplateId = defaultTemplateId;
}
}
private void MapComposition(IDictionary<int, IContentTypeComposition> contentTypes)
{
// get parent/child
var sql1 = Sql()
.Select<ContentType2ContentTypeDto>()
.From<ContentType2ContentTypeDto>()
.OrderBy<ContentType2ContentTypeDto>(x => x.ChildId);
var compositionDtos = Database.Fetch<ContentType2ContentTypeDto>(sql1);
// map
var compositionIx = 0;
foreach (var contentType in contentTypes.Values)
{
while (compositionIx < compositionDtos.Count && compositionDtos[compositionIx].ChildId == contentType.Id)
{
var parentDto = compositionDtos[compositionIx];
if (!contentTypes.TryGetValue(parentDto.ParentId, out var parentContentType)) continue;
contentType.AddContentType(parentContentType);
compositionIx++;
}
}
}
private void MapGroupsAndProperties(IDictionary<int, IContentTypeComposition> contentTypes)
{
var sql1 = Sql()
.Select<PropertyTypeGroupDto>()
.From<PropertyTypeGroupDto>()
.InnerJoin<ContentTypeDto>().On<PropertyTypeGroupDto, ContentTypeDto>((ptg, ct) => ptg.ContentTypeNodeId == ct.NodeId)
.OrderBy<ContentTypeDto>(x => x.NodeId)
.AndBy<PropertyTypeGroupDto>(x => x.SortOrder, x => x.Id);
var groupDtos = Database.Fetch<PropertyTypeGroupDto>(sql1);
var sql2 = Sql()
.Select<PropertyTypeDto>(r => r.Select(x => x.DataTypeDto))
.AndSelect<MemberPropertyTypeDto>()
.From<PropertyTypeDto>()
.InnerJoin<DataTypeDto>().On<PropertyTypeDto, DataTypeDto>((pt, dt) => pt.DataTypeId == dt.NodeId)
.InnerJoin<ContentTypeDto>().On<PropertyTypeDto, ContentTypeDto>((pt, ct) => pt.ContentTypeId == ct.NodeId)
.LeftJoin<PropertyTypeGroupDto>().On<PropertyTypeDto, PropertyTypeGroupDto>((pt, ptg) => pt.PropertyTypeGroupId == ptg.Id)
.LeftJoin<MemberPropertyTypeDto>().On<PropertyTypeDto, MemberPropertyTypeDto>((pt, mpt) => pt.Id == mpt.PropertyTypeId)
.OrderBy<ContentTypeDto>(x => x.NodeId)
.AndBy<PropertyTypeGroupDto>(x => x.SortOrder, x => x.Id) // NULLs will come first or last, never mind, we deal with it below
.AndBy<PropertyTypeDto>(x => x.SortOrder, x => x.Id);
var propertyDtos = Database.Fetch<PropertyTypeCommonDto>(sql2);
var builtinProperties = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
var groupIx = 0;
var propertyIx = 0;
foreach (var contentType in contentTypes.Values)
{
// only IContentType is publishing
var isPublishing = contentType is IContentType;
// get group-less properties (in case NULL is ordered first)
var noGroupPropertyTypes = new PropertyTypeCollection(isPublishing);
while (propertyIx < propertyDtos.Count && propertyDtos[propertyIx].ContentTypeId == contentType.Id && propertyDtos[propertyIx].PropertyTypeGroupId == null)
{
noGroupPropertyTypes.Add(MapPropertyType(contentType, propertyDtos[propertyIx], builtinProperties));
propertyIx++;
}
// get groups and their properties
var groupCollection = new PropertyGroupCollection();
while (groupIx < groupDtos.Count && groupDtos[groupIx].ContentTypeNodeId == contentType.Id)
{
var group = MapPropertyGroup(groupDtos[groupIx], isPublishing);
groupCollection.Add(group);
groupIx++;
while (propertyIx < propertyDtos.Count && propertyDtos[propertyIx].ContentTypeId == contentType.Id && propertyDtos[propertyIx].PropertyTypeGroupId == group.Id)
{
group.PropertyTypes.Add(MapPropertyType(contentType, propertyDtos[propertyIx], builtinProperties));
propertyIx++;
}
}
contentType.PropertyGroups = groupCollection;
// get group-less properties (in case NULL is ordered last)
while (propertyIx < propertyDtos.Count && propertyDtos[propertyIx].ContentTypeId == contentType.Id && propertyDtos[propertyIx].PropertyTypeGroupId == null)
{
noGroupPropertyTypes.Add(MapPropertyType(contentType, propertyDtos[propertyIx], builtinProperties));
propertyIx++;
}
contentType.NoGroupPropertyTypes = noGroupPropertyTypes;
}
}
private PropertyGroup MapPropertyGroup(PropertyTypeGroupDto dto, bool isPublishing)
{
return new PropertyGroup(new PropertyTypeCollection(isPublishing))
{
Id = dto.Id,
Name = dto.Text,
SortOrder = dto.SortOrder,
Key = dto.UniqueId
};
}
private PropertyType MapPropertyType(IContentTypeComposition contentType, PropertyTypeCommonDto dto, IDictionary<string, PropertyType> builtinProperties)
{
var groupId = dto.PropertyTypeGroupId;
var readonlyStorageType = builtinProperties.TryGetValue(dto.Alias, out var propertyType);
var storageType = readonlyStorageType ? propertyType.ValueStorageType : Enum<ValueStorageType>.Parse(dto.DataTypeDto.DbType);
if (contentType is MemberType memberType)
{
var access = new MemberTypePropertyProfileAccess(dto.ViewOnProfile, dto.CanEdit, dto.IsSensitive);
memberType.MemberTypePropertyTypes.Add(dto.Alias, access);
}
return new PropertyType(dto.DataTypeDto.EditorAlias, storageType, readonlyStorageType, dto.Alias)
{
Description = dto.Description,
DataTypeId = dto.DataTypeId,
Id = dto.Id,
Key = dto.UniqueId,
Mandatory = dto.Mandatory,
Name = dto.Name,
PropertyGroupId = groupId.HasValue ? new Lazy<int>(() => groupId.Value) : null,
SortOrder = dto.SortOrder,
ValidationRegExp = dto.ValidationRegExp,
Variations = (ContentVariation)dto.Variations
};
}
}
}
@@ -17,13 +17,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
/// </summary>
internal class ContentTypeRepository : ContentTypeRepositoryBase<IContentType>, IContentTypeRepository
{
private readonly ITemplateRepository _templateRepository;
public ContentTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, ITemplateRepository templateRepository)
: base(scopeAccessor, cache, logger)
{
_templateRepository = templateRepository;
}
public ContentTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
: base(scopeAccessor, cache, logger, commonRepository)
{ }
protected override bool SupportsPublishing => ContentType.SupportsPublishingConst;
@@ -32,75 +28,81 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return new FullDataSetRepositoryCachePolicy<IContentType, int>(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ true);
}
// every GetExists method goes cachePolicy.GetSomething which in turns goes PerformGetAll,
// since this is a FullDataSet policy - and everything is cached
// so here,
// every PerformGet/Exists just GetMany() and then filters
// except PerformGetAll which is the one really doing the job
// TODO: the filtering is highly inefficient as we deep-clone everything
// there should be a way to GetMany(predicate) right from the cache policy!
// and ah, well, this all caching should be refactored + the cache refreshers
// should to repository.Clear() not deal with magic caches by themselves
protected override IContentType PerformGet(int id)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Id == id);
}
=> GetMany().FirstOrDefault(x => x.Id == id);
protected override IContentType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Key == id);
}
=> GetMany().FirstOrDefault(x => x.Key == id);
protected override IContentType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
protected override bool PerformExists(Guid id)
{
return GetMany().FirstOrDefault(x => x.Key == id) != null;
}
=> GetMany().FirstOrDefault(x => x.Key == id) != null;
protected override IEnumerable<IContentType> PerformGetAll(params int[] ids)
{
if (ids.Any())
{
//NOTE: This logic should never be executed according to our cache policy
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, SupportsPublishing, this, _templateRepository)
.Where(x => ids.Contains(x.Id));
}
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, SupportsPublishing, this, _templateRepository);
// the cache policy will always want everything
// even GetMany(ids) gets everything and filters afterwards
if (ids.Any()) throw new Exception("panic");
return CommonRepository.GetAllTypes().OfType<IContentType>();
}
protected override IEnumerable<IContentType> PerformGetAll(params Guid[] ids)
{
// use the underlying GetAll which will force cache all content types
return ids.Any() ? GetMany().Where(x => ids.Contains(x.Key)) : GetMany();
var all = GetMany();
return ids.Any() ? all.Where(x => ids.Contains(x.Key)) : all;
}
protected override IEnumerable<IContentType> PerformGetByQuery(IQuery<IContentType> query)
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContentType>(sqlClause, query);
var baseQuery = GetBaseQuery(false);
var translator = new SqlTranslator<IContentType>(baseQuery, query);
var sql = translator.Translate();
var ids = Database.Fetch<int>(sql).Distinct().ToArray();
var dtos = Database.Fetch<ContentTypeTemplateDto>(sql);
return
//This returns a lookup from the GetAll cached lookup
(dtos.Any()
? GetMany(dtos.DistinctBy(x => x.ContentTypeDto.NodeId).Select(x => x.ContentTypeDto.NodeId).ToArray())
: Enumerable.Empty<IContentType>())
//order the result by name
.OrderBy(x => x.Name);
return ids.Length > 0 ? GetMany(ids).OrderBy(x => x.Name) : Enumerable.Empty<IContentType>();
}
/// <summary>
/// Gets all entities of the specified <see cref="PropertyType"/> query
/// </summary>
/// <param name="query"></param>
/// <returns>An enumerable list of <see cref="IContentType"/> objects</returns>
/// <inheritdoc />
public IEnumerable<IContentType> GetByQuery(IQuery<PropertyType> query)
{
var ints = PerformGetByQuery(query).ToArray();
return ints.Any()
? GetMany(ints)
: Enumerable.Empty<IContentType>();
return ints.Length > 0 ? GetMany(ints) : Enumerable.Empty<IContentType>();
}
protected IEnumerable<int> PerformGetByQuery(IQuery<PropertyType> query)
{
// used by DataTypeService to remove properties
// from content types if they have a deleted data type - see
// notes in DataTypeService.Delete as it's a bit weird
var sqlClause = Sql()
.SelectAll()
.From<PropertyTypeGroupDto>()
.RightJoin<PropertyTypeDto>()
.On<PropertyTypeGroupDto, PropertyTypeDto>(left => left.Id, right => right.PropertyTypeGroupId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.NodeId);
var translator = new SqlTranslator<PropertyType>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<PropertyTypeDto>(x => x.PropertyTypeGroupId);
return Database
.FetchOneToMany<PropertyTypeGroupDto>(x => x.PropertyTypeDtos, sql)
.Select(x => x.ContentTypeNodeId).Distinct();
}
/// <summary>
@@ -141,7 +143,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (aliases.Length == 0) return Enumerable.Empty<int>();
var sql = Sql()
.Select("cmsContentType.nodeId")
.Select<ContentTypeDto>(x => x.NodeId)
.From<ContentTypeDto>()
.InnerJoin<NodeDto>()
.On<ContentTypeDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
@@ -156,14 +158,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
sql = isCount
? sql.SelectCount()
: sql.Select<ContentTypeTemplateDto>(r => r.Select(x => x.ContentTypeDto, r1 => r1.Select(x => x.NodeDto)));
: sql.Select<ContentTypeDto>(x => x.NodeId);
sql
.From<ContentTypeDto>()
.InnerJoin<NodeDto>()
.On<ContentTypeDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.LeftJoin<ContentTypeTemplateDto>()
.On<ContentTypeTemplateDto, ContentTypeDto>(left => left.ContentTypeNodeId, right => right.NodeId)
.InnerJoin<NodeDto>().On<ContentTypeDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.LeftJoin<ContentTypeTemplateDto>().On<ContentTypeTemplateDto, ContentTypeDto>(left => left.ContentTypeNodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
@@ -10,11 +10,9 @@ using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
@@ -28,9 +26,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
internal abstract class ContentTypeRepositoryBase<TEntity> : NPocoRepositoryBase<int, TEntity>, IReadRepository<Guid, TEntity>
where TEntity : class, IContentTypeComposition
{
protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
: base(scopeAccessor, cache, logger)
{ }
{
CommonRepository = commonRepository;
}
protected IContentTypeCommonRepository CommonRepository { get; }
protected abstract bool SupportsPublishing { get; }
@@ -61,7 +63,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// move to parent (or -1), update path, save
moving.ParentId = parentId;
var movingPath = moving.Path + ","; // save before changing
moving.Path = (container == null ? Constants.System.Root.ToString() : container.Path) + "," + moving.Id;
moving.Path = (container == null ? Constants.System.RootString : container.Path) + "," + moving.Id;
moving.Level = container == null ? 1 : container.Level + 1;
Save(moving);
@@ -82,38 +84,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return moveInfo;
}
/// <summary>
/// Returns the content type ids that match the query
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
protected IEnumerable<int> PerformGetByQuery(IQuery<PropertyType> query)
protected virtual PropertyType CreatePropertyType(string propertyEditorAlias, ValueStorageType storageType, string propertyTypeAlias)
{
// used by DataTypeDefinitionRepository to remove properties
// from content types if they have a deleted data type - see
// notes in DataTypeDefinitionRepository.Delete as it's a bit
// weird
var sqlClause = Sql()
.SelectAll()
.From<PropertyTypeGroupDto>()
.RightJoin<PropertyTypeDto>()
.On<PropertyTypeGroupDto, PropertyTypeDto>(left => left.Id, right => right.PropertyTypeGroupId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.NodeId);
var translator = new SqlTranslator<PropertyType>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<PropertyTypeDto>(x => x.PropertyTypeGroupId);
return Database
.FetchOneToMany<PropertyTypeGroupDto>(x => x.PropertyTypeDtos, sql)
.Select(x => x.ContentTypeNodeId).Distinct();
return new PropertyType(propertyEditorAlias, storageType, propertyTypeAlias);
}
protected virtual PropertyType CreatePropertyType(string propertyEditorAlias, ValueStorageType dbType, string propertyTypeAlias)
protected override void PersistDeletedItem(TEntity entity)
{
return new PropertyType(propertyEditorAlias, dbType, propertyTypeAlias);
base.PersistDeletedItem(entity);
CommonRepository.ClearCache(); // always
}
protected void PersistNewBaseContentType(IContentTypeComposition entity)
@@ -228,6 +208,8 @@ AND umbracoNode.nodeObjectType = @objectType",
propertyType.PropertyEditorAlias = dataTypeDto.EditorAlias;
propertyType.ValueStorageType = dataTypeDto.DbType.EnumParse<ValueStorageType>(true);
}
CommonRepository.ClearCache(); // always
}
protected void PersistUpdatedBaseContentType(IContentTypeComposition entity)
@@ -532,6 +514,8 @@ AND umbracoNode.id <> @id",
if (orphanPropertyTypeIds != null)
foreach (var id in orphanPropertyTypeIds)
DeletePropertyType(entity.Id, id);
CommonRepository.ClearCache(); // always
}
private IEnumerable<IContentTypeComposition> GetImpactedContentTypes(IContentTypeComposition contentType, IEnumerable<IContentTypeComposition> all)
@@ -992,74 +976,6 @@ AND umbracoNode.id <> @id",
new { Id = contentTypeId, PropertyTypeId = propertyTypeId });
}
protected IEnumerable<ContentTypeSort> GetAllowedContentTypeIds(int id)
{
var sql = Sql()
.SelectAll()
.From<ContentTypeAllowedContentTypeDto>()
.LeftJoin<ContentTypeDto>()
.On<ContentTypeAllowedContentTypeDto, ContentTypeDto>(left => left.AllowedId, right => right.NodeId)
.Where<ContentTypeAllowedContentTypeDto>(x => x.Id == id);
var allowedContentTypeDtos = Database.Fetch<ContentTypeAllowedContentTypeDto>(sql);
return allowedContentTypeDtos.Select(x => new ContentTypeSort(new Lazy<int>(() => x.AllowedId), x.SortOrder, x.ContentTypeDto.Alias)).ToList();
}
protected PropertyGroupCollection GetPropertyGroupCollection(int id, DateTime createDate, DateTime updateDate)
{
var sql = Sql()
.SelectAll()
.From<PropertyTypeGroupDto>()
.LeftJoin<PropertyTypeDto>()
.On<PropertyTypeGroupDto, PropertyTypeDto>(left => left.Id, right => right.PropertyTypeGroupId)
.LeftJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.NodeId)
.Where<PropertyTypeGroupDto>(x => x.ContentTypeNodeId == id)
.OrderBy<PropertyTypeGroupDto>(x => x.Id);
var dtos = Database
.Fetch<PropertyTypeGroupDto>(sql);
var propertyGroups = PropertyGroupFactory.BuildEntity(dtos, SupportsPublishing, id, createDate, updateDate,CreatePropertyType);
return new PropertyGroupCollection(propertyGroups);
}
protected PropertyTypeCollection GetPropertyTypeCollection(int id, DateTime createDate, DateTime updateDate)
{
var sql = Sql()
.SelectAll()
.From<PropertyTypeDto>()
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.NodeId)
.Where<PropertyTypeDto>(x => x.ContentTypeId == id);
var dtos = Database.Fetch<PropertyTypeDto>(sql);
// TODO: Move this to a PropertyTypeFactory
var list = new List<PropertyType>();
foreach (var dto in dtos.Where(x => x.PropertyTypeGroupId <= 0))
{
var propType = CreatePropertyType(dto.DataTypeDto.EditorAlias, dto.DataTypeDto.DbType.EnumParse<ValueStorageType>(true), dto.Alias);
propType.DataTypeId = dto.DataTypeId;
propType.Description = dto.Description;
propType.Id = dto.Id;
propType.Key = dto.UniqueId;
propType.Name = dto.Name;
propType.Mandatory = dto.Mandatory;
propType.SortOrder = dto.SortOrder;
propType.ValidationRegExp = dto.ValidationRegExp;
propType.CreateDate = createDate;
propType.UpdateDate = updateDate;
list.Add(propType);
}
//Reset dirty properties
Parallel.ForEach(list, currentFile => currentFile.ResetDirtyProperties(false));
return new PropertyTypeCollection(SupportsPublishing, list);
}
protected void ValidateAlias(PropertyType pt)
{
if (string.IsNullOrWhiteSpace(pt.Alias))
@@ -1114,589 +1030,6 @@ AND umbracoNode.id <> @id",
}
}
internal static class ContentTypeQueryMapper
{
public class AssociatedTemplate
{
public AssociatedTemplate(int templateId, string alias, string templateName)
{
TemplateId = templateId;
Alias = alias;
TemplateName = templateName;
}
public int TemplateId { get; set; }
public string Alias { get; set; }
public string TemplateName { get; set; }
protected bool Equals(AssociatedTemplate other)
{
return TemplateId == other.TemplateId;
}
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((AssociatedTemplate)obj);
}
public override int GetHashCode()
{
return TemplateId;
}
}
public static IEnumerable<IMediaType> GetMediaTypes<TRepo>(
IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository)
where TRepo : IReadRepository<int, TEntity>
{
IDictionary<int, List<int>> allParentMediaTypeIds;
var mediaTypes = MapMediaTypes(db, sqlSyntax, out allParentMediaTypeIds)
.ToArray();
MapContentTypeChildren(mediaTypes, db, sqlSyntax, isPublishing, contentTypeRepository, allParentMediaTypeIds);
return mediaTypes;
}
public static IEnumerable<IContentType> GetContentTypes<TRepo>(
IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository,
ITemplateRepository templateRepository)
where TRepo : IReadRepository<int, TEntity>
{
IDictionary<int, List<AssociatedTemplate>> allAssociatedTemplates;
IDictionary<int, List<int>> allParentContentTypeIds;
var contentTypes = MapContentTypes(db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
.ToArray();
if (contentTypes.Any())
{
MapContentTypeTemplates(
contentTypes, db, contentTypeRepository, templateRepository, allAssociatedTemplates);
MapContentTypeChildren(contentTypes, db, sqlSyntax, isPublishing, contentTypeRepository, allParentContentTypeIds);
}
return contentTypes;
}
internal static void MapContentTypeChildren<TRepo>(IContentTypeComposition[] contentTypes,
IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
TRepo contentTypeRepository,
IDictionary<int, List<int>> allParentContentTypeIds)
where TRepo : IReadRepository<int, TEntity>
{
//NOTE: SQL call #2
var ids = contentTypes.Select(x => x.Id).ToArray();
IDictionary<int, PropertyGroupCollection> allPropGroups;
IDictionary<int, PropertyTypeCollection> allPropTypes;
MapGroupsAndProperties(ids, db, sqlSyntax, isPublishing, out allPropTypes, out allPropGroups);
foreach (var contentType in contentTypes)
{
contentType.PropertyGroups = allPropGroups[contentType.Id];
contentType.NoGroupPropertyTypes = allPropTypes[contentType.Id];
}
//NOTE: SQL call #3++
if (allParentContentTypeIds != null)
{
var allParentIdsAsArray = allParentContentTypeIds.SelectMany(x => x.Value).Distinct().ToArray();
if (allParentIdsAsArray.Any())
{
var allParentContentTypes = contentTypes.Where(x => allParentIdsAsArray.Contains(x.Id)).ToArray();
foreach (var contentType in contentTypes)
{
var entityId = contentType.Id;
var parentContentTypes = allParentContentTypes.Where(x =>
{
var parentEntityId = x.Id;
return allParentContentTypeIds[entityId].Contains(parentEntityId);
});
foreach (var parentContentType in parentContentTypes)
{
var result = contentType.AddContentType(parentContentType);
//Do something if adding fails? (Should hopefully not be possible unless someone created a circular reference)
}
// reset dirty initial properties (U4-1946)
((EntityBase)contentType).ResetDirtyProperties(false);
}
}
}
}
internal static void MapContentTypeTemplates<TRepo>(IContentType[] contentTypes,
IDatabase db,
TRepo contentTypeRepository,
ITemplateRepository templateRepository,
IDictionary<int, List<AssociatedTemplate>> associatedTemplates)
where TRepo : IReadRepository<int, TEntity>
{
if (associatedTemplates == null || associatedTemplates.Any() == false) return;
//NOTE: SQL call #3++
//SEE: http://issues.umbraco.org/issue/U4-5174 to fix this
var templateIds = associatedTemplates.SelectMany(x => x.Value).Select(x => x.TemplateId)
.Distinct()
.ToArray();
var templates = (templateIds.Any()
? templateRepository.GetMany(templateIds)
: Enumerable.Empty<ITemplate>()).ToArray();
foreach (var contentType in contentTypes)
{
var entityId = contentType.Id;
var associatedTemplateIds = associatedTemplates[entityId].Select(x => x.TemplateId)
.Distinct()
.ToArray();
contentType.AllowedTemplates = (associatedTemplateIds.Any()
? templates.Where(x => associatedTemplateIds.Contains(x.Id))
: Enumerable.Empty<ITemplate>()).ToArray();
}
}
internal static IEnumerable<IMediaType> MapMediaTypes(IDatabase db, ISqlSyntaxProvider sqlSyntax,
out IDictionary<int, List<int>> parentMediaTypeIds)
{
if (db == null) throw new ArgumentNullException(nameof(db));
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.variations as ctVariations,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.IsElement as ctIsElement, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId
FROM cmsContentType
INNER JOIN umbracoNode
ON cmsContentType.nodeId = umbracoNode.id
LEFT JOIN (
SELECT cmsContentTypeAllowedContentType.Id, cmsContentTypeAllowedContentType.AllowedId, cmsContentType.alias, cmsContentTypeAllowedContentType.SortOrder
FROM cmsContentTypeAllowedContentType
INNER JOIN cmsContentType
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
) AllowedTypes
ON AllowedTypes.Id = cmsContentType.nodeId
LEFT JOIN (
SELECT cmsContentType2ContentType.parentContentTypeId, umbracoNode.uniqueID AS parentContentTypeKey, cmsContentType2ContentType.childContentTypeId
FROM cmsContentType2ContentType
INNER JOIN umbracoNode
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
) ParentTypes
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
ORDER BY ctId";
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = Constants.ObjectTypes.MediaType });
if (result.Any() == false)
{
parentMediaTypeIds = null;
return Enumerable.Empty<IMediaType>();
}
parentMediaTypeIds = new Dictionary<int, List<int>>();
var mappedMediaTypes = new List<IMediaType>();
//loop through each result and fill in our required values, each row will contain different required data than the rest.
// it is much quicker to iterate each result and populate instead of looking up the values over and over in the result like
// we used to do.
var queue = new Queue<dynamic>(result);
var currAllowedContentTypes = new List<ContentTypeSort>();
while (queue.Count > 0)
{
var ct = queue.Dequeue();
//check for allowed content types
int? allowedCtId = ct.ctaAllowedId;
int? allowedCtSort = ct.ctaSortOrder;
string allowedCtAlias = ct.ctaAlias;
if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null)
{
var ctSort = new ContentTypeSort(new Lazy<int>(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias);
if (currAllowedContentTypes.Contains(ctSort) == false)
{
currAllowedContentTypes.Add(ctSort);
}
}
//always ensure there's a list for this content type
if (parentMediaTypeIds.ContainsKey(ct.ctId) == false)
parentMediaTypeIds[ct.ctId] = new List<int>();
//check for parent ids and assign to the outgoing collection
int? parentId = ct.chtParentId;
if (parentId.HasValue)
{
var associatedParentIds = parentMediaTypeIds[ct.ctId];
if (associatedParentIds.Contains(parentId.Value) == false)
associatedParentIds.Add(parentId.Value);
}
if (queue.Count == 0 || queue.Peek().ctId != ct.ctId)
{
//it's the last in the queue or the content type is changing (moving to the next one)
var mediaType = CreateForMapping(ct, currAllowedContentTypes);
mappedMediaTypes.Add(mediaType);
//Here we need to reset the current variables, we're now collecting data for a different content type
currAllowedContentTypes = new List<ContentTypeSort>();
}
}
return mappedMediaTypes;
}
private static IMediaType CreateForMapping(dynamic currCt, List<ContentTypeSort> currAllowedContentTypes)
{
// * create the DTO object
// * create the content type object
// * map the allowed content types
// * add to the outgoing list
var contentTypeDto = new ContentTypeDto
{
Alias = currCt.ctAlias,
AllowAtRoot = currCt.ctAllowAtRoot,
Description = currCt.ctDesc,
Icon = currCt.ctIcon,
IsContainer = currCt.ctIsContainer,
IsElement = currCt.ctIsElement,
NodeId = currCt.ctId,
PrimaryKey = currCt.ctPk,
Thumbnail = currCt.ctThumb,
Variations = (byte) currCt.ctVariations,
//map the underlying node dto
NodeDto = new NodeDto
{
CreateDate = currCt.nCreateDate,
Level = (short)currCt.nLevel,
NodeId = currCt.ctId,
NodeObjectType = currCt.nObjectType,
ParentId = currCt.nParentId,
Path = currCt.nPath,
SortOrder = currCt.nSortOrder,
Text = currCt.nName,
Trashed = currCt.nTrashed,
UniqueId = currCt.nUniqueId,
UserId = currCt.nUser
}
};
//now create the content type object;
var mediaType = ContentTypeFactory.BuildMediaTypeEntity(contentTypeDto);
//map the allowed content types
mediaType.AllowedContentTypes = currAllowedContentTypes;
return mediaType;
}
internal static IEnumerable<IContentType> MapContentTypes(IDatabase db, ISqlSyntaxProvider sqlSyntax,
out IDictionary<int, List<AssociatedTemplate>> associatedTemplates,
out IDictionary<int, List<int>> parentContentTypeIds)
{
if (db == null) throw new ArgumentNullException(nameof(db));
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.variations as ctVariations,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.IsElement as ctIsElement, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId,
Template.alias as tAlias, Template.nodeId as tId,Template.text as tText
FROM cmsContentType
INNER JOIN umbracoNode
ON cmsContentType.nodeId = umbracoNode.id
LEFT JOIN cmsDocumentType
ON cmsDocumentType.contentTypeNodeId = cmsContentType.nodeId
LEFT JOIN (
SELECT cmsContentTypeAllowedContentType.Id, cmsContentTypeAllowedContentType.AllowedId, cmsContentType.alias, cmsContentTypeAllowedContentType.SortOrder
FROM cmsContentTypeAllowedContentType
INNER JOIN cmsContentType
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
) AllowedTypes
ON AllowedTypes.Id = cmsContentType.nodeId
LEFT JOIN (
SELECT * FROM cmsTemplate
INNER JOIN umbracoNode
ON cmsTemplate.nodeId = umbracoNode.id
) as Template
ON Template.nodeId = cmsDocumentType.templateNodeId
LEFT JOIN (
SELECT cmsContentType2ContentType.parentContentTypeId, umbracoNode.uniqueID AS parentContentTypeKey, cmsContentType2ContentType.childContentTypeId
FROM cmsContentType2ContentType
INNER JOIN umbracoNode
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
) ParentTypes
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
ORDER BY ctId";
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = Constants.ObjectTypes.DocumentType });
if (result.Any() == false)
{
parentContentTypeIds = null;
associatedTemplates = null;
return Enumerable.Empty<IContentType>();
}
parentContentTypeIds = new Dictionary<int, List<int>>();
associatedTemplates = new Dictionary<int, List<AssociatedTemplate>>();
var mappedContentTypes = new List<IContentType>();
var queue = new Queue<dynamic>(result);
var currDefaultTemplate = -1;
var currAllowedContentTypes = new List<ContentTypeSort>();
while (queue.Count > 0)
{
var ct = queue.Dequeue();
//check for default templates
bool? isDefaultTemplate = Convert.ToBoolean(ct.dtIsDefault);
int? templateId = ct.dtTemplateId;
if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && isDefaultTemplate.Value && templateId.HasValue)
{
currDefaultTemplate = templateId.Value;
}
//always ensure there's a list for this content type
if (associatedTemplates.ContainsKey(ct.ctId) == false)
associatedTemplates[ct.ctId] = new List<AssociatedTemplate>();
//check for associated templates and assign to the outgoing collection
if (ct.tId != null)
{
var associatedTemplate = new AssociatedTemplate(ct.tId, ct.tAlias, ct.tText);
var associatedList = associatedTemplates[ct.ctId];
if (associatedList.Contains(associatedTemplate) == false)
associatedList.Add(associatedTemplate);
}
//check for allowed content types
int? allowedCtId = ct.ctaAllowedId;
int? allowedCtSort = ct.ctaSortOrder;
string allowedCtAlias = ct.ctaAlias;
if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null)
{
var ctSort = new ContentTypeSort(new Lazy<int>(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias);
if (currAllowedContentTypes.Contains(ctSort) == false)
{
currAllowedContentTypes.Add(ctSort);
}
}
//always ensure there's a list for this content type
if (parentContentTypeIds.ContainsKey(ct.ctId) == false)
parentContentTypeIds[ct.ctId] = new List<int>();
//check for parent ids and assign to the outgoing collection
int? parentId = ct.chtParentId;
if (parentId.HasValue)
{
var associatedParentIds = parentContentTypeIds[ct.ctId];
if (associatedParentIds.Contains(parentId.Value) == false)
associatedParentIds.Add(parentId.Value);
}
if (queue.Count == 0 || queue.Peek().ctId != ct.ctId)
{
//it's the last in the queue or the content type is changing (moving to the next one)
var contentType = CreateForMapping(ct, currAllowedContentTypes, currDefaultTemplate);
mappedContentTypes.Add(contentType);
//Here we need to reset the current variables, we're now collecting data for a different content type
currDefaultTemplate = -1;
currAllowedContentTypes = new List<ContentTypeSort>();
}
}
return mappedContentTypes;
}
private static IContentType CreateForMapping(dynamic currCt, List<ContentTypeSort> currAllowedContentTypes, int currDefaultTemplate)
{
// * set the default template to the first one if a default isn't found
// * create the DTO object
// * create the content type object
// * map the allowed content types
// * add to the outgoing list
var dtDto = new ContentTypeTemplateDto
{
//create the content type dto
ContentTypeDto = new ContentTypeDto
{
Alias = currCt.ctAlias,
AllowAtRoot = currCt.ctAllowAtRoot,
Description = currCt.ctDesc,
Icon = currCt.ctIcon,
IsContainer = currCt.ctIsContainer,
IsElement = currCt.ctIsElement,
NodeId = currCt.ctId,
PrimaryKey = currCt.ctPk,
Thumbnail = currCt.ctThumb,
Variations = (byte) currCt.ctVariations,
//map the underlying node dto
NodeDto = new NodeDto
{
CreateDate = currCt.nCreateDate,
Level = (short)currCt.nLevel,
NodeId = currCt.ctId,
NodeObjectType = currCt.nObjectType,
ParentId = currCt.nParentId,
Path = currCt.nPath,
SortOrder = currCt.nSortOrder,
Text = currCt.nName,
Trashed = currCt.nTrashed,
UniqueId = currCt.nUniqueId,
UserId = currCt.nUser
}
},
ContentTypeNodeId = currCt.ctId,
IsDefault = currDefaultTemplate != -1,
TemplateNodeId = currDefaultTemplate != -1 ? currDefaultTemplate : 0,
};
//now create the content type object
var contentType = ContentTypeFactory.BuildContentTypeEntity(dtDto.ContentTypeDto);
// NOTE
// that was done by the factory but makes little sense, moved here, so
// now we have to reset dirty props again (as the factory does it) and yet,
// we are not managing allowed templates... the whole thing is weird.
((ContentType)contentType).DefaultTemplateId = dtDto.TemplateNodeId;
contentType.ResetDirtyProperties(false);
//map the allowed content types
contentType.AllowedContentTypes = currAllowedContentTypes;
return contentType;
}
internal static void MapGroupsAndProperties(int[] contentTypeIds, IDatabase db, ISqlSyntaxProvider sqlSyntax, bool isPublishing,
out IDictionary<int, PropertyTypeCollection> allPropertyTypeCollection,
out IDictionary<int, PropertyGroupCollection> allPropertyGroupCollection)
{
allPropertyGroupCollection = new Dictionary<int, PropertyGroupCollection>();
allPropertyTypeCollection = new Dictionary<int, PropertyTypeCollection>();
// query below is not safe + pointless if array is empty
if (contentTypeIds.Length == 0) return;
var sqlGroups = @"SELECT
pg.contenttypeNodeId AS contentTypeId,
pg.id AS id, pg.uniqueID AS " + sqlSyntax.GetQuotedColumnName("key") + @",
pg.sortOrder AS sortOrder, pg." + sqlSyntax.GetQuotedColumnName("text") + @" AS text
FROM cmsPropertyTypeGroup pg
WHERE pg.contenttypeNodeId IN (@ids)
ORDER BY contentTypeId, id";
var sqlProps = @"SELECT
pt.contentTypeId AS contentTypeId,
pt.id AS id, pt.uniqueID AS " + sqlSyntax.GetQuotedColumnName("key") + @",
pt.propertyTypeGroupId AS groupId,
pt.Alias AS alias, pt." + sqlSyntax.GetQuotedColumnName("Description") + @" AS " + sqlSyntax.GetQuotedColumnName("desc") + $@", pt.mandatory AS mandatory,
pt.Name AS name, pt.sortOrder AS sortOrder, pt.validationRegExp AS regexp, pt.variations as variations,
dt.nodeId as dataTypeId, dt.dbType as dbType, dt.propertyEditorAlias as editorAlias
FROM cmsPropertyType pt
INNER JOIN {Constants.DatabaseSchema.Tables.DataType} as dt ON pt.dataTypeId = dt.nodeId
WHERE pt.contentTypeId IN (@ids)
ORDER BY contentTypeId, groupId, id";
if (contentTypeIds.Length > 2000)
throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters");
var groups = db.Fetch<dynamic>(sqlGroups, new { ids = contentTypeIds });
var groupsEnumerator = groups.GetEnumerator();
var group = groupsEnumerator.MoveNext() ? groupsEnumerator.Current : null;
var props = db.Fetch<dynamic>(sqlProps, new { ids = contentTypeIds });
var propsEnumerator = props.GetEnumerator();
var prop = propsEnumerator.MoveNext() ? propsEnumerator.Current : null;
// groups are ordered by content type, group id
// props are ordered by content type, group id, prop id
foreach (var contentTypeId in contentTypeIds)
{
var propertyTypeCollection = allPropertyTypeCollection[contentTypeId] = new PropertyTypeCollection(isPublishing);
var propertyGroupCollection = allPropertyGroupCollection[contentTypeId] = new PropertyGroupCollection();
while (prop != null && prop.contentTypeId == contentTypeId && prop.groupId == null)
{
AddPropertyType(propertyTypeCollection, prop);
prop = propsEnumerator.MoveNext() ? propsEnumerator.Current : null;
}
while (group != null && group.contentTypeId == contentTypeId)
{
var propertyGroup = new PropertyGroup(new PropertyTypeCollection(isPublishing))
{
Id = group.id,
Name = group.text,
SortOrder = group.sortOrder,
Key = group.key
};
propertyGroupCollection.Add(propertyGroup);
while (prop != null && prop.groupId == group.id)
{
AddPropertyType(propertyGroup.PropertyTypes, prop, propertyGroup);
prop = propsEnumerator.MoveNext() ? propsEnumerator.Current : null;
}
group = groupsEnumerator.MoveNext() ? groupsEnumerator.Current : null;
}
}
propsEnumerator.Dispose();
groupsEnumerator.Dispose();
}
private static void AddPropertyType(PropertyTypeCollection propertyTypes, dynamic prop, PropertyGroup propertyGroup = null)
{
var propertyType = new PropertyType(prop.editorAlias, Enum<ValueStorageType>.Parse(prop.dbType), prop.alias)
{
Description = prop.desc,
DataTypeId = prop.dataTypeId,
Id = prop.id,
Key = prop.key,
Mandatory = Convert.ToBoolean(prop.mandatory),
Name = prop.name,
PropertyGroupId = propertyGroup == null ? null : new Lazy<int>(() => propertyGroup.Id),
SortOrder = prop.sortOrder,
ValidationRegExp = prop.regexp,
Variations = (ContentVariation) prop.variations
};
propertyTypes.Add(propertyType);
}
}
protected abstract TEntity PerformGet(Guid id);
protected abstract TEntity PerformGet(string alias);
protected abstract IEnumerable<TEntity> PerformGetAll(params Guid[] ids);
@@ -232,7 +232,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.OrderByDescending<ContentVersionDto>(x => x.Current)
.AndByDescending<ContentVersionDto>(x => x.VersionDate);
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true, true);
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true, true).Skip(skip).Take(take);
}
public override IContent GetVersion(int versionId)
@@ -46,7 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IIdentityUserLogin PerformGet(int id)
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
sql.Where(GetBaseWhereClause(), new { id = id });
var dto = Database.Fetch<ExternalLoginDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
@@ -0,0 +1,11 @@
namespace Umbraco.Core.Persistence.Repositories.Implement
{
internal static class LanguageRepositoryExtensions
{
public static bool IsDefault(this ILanguageRepository repo, string culture)
{
if (culture == null || culture == "*") return false;
return repo.GetDefaultIsoCode().InvariantEquals(culture);
}
}
}
@@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
/// </summary>
internal class MediaTypeRepository : ContentTypeRepositoryBase<IMediaType>, IMediaTypeRepository
{
public MediaTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
: base(scopeAccessor, cache, logger)
public MediaTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
: base(scopeAccessor, cache, logger, commonRepository)
{ }
protected override bool SupportsPublishing => MediaType.SupportsPublishingConst;
@@ -27,83 +27,46 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return new FullDataSetRepositoryCachePolicy<IMediaType, int>(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ true);
}
// every GetExists method goes cachePolicy.GetSomething which in turns goes PerformGetAll,
// since this is a FullDataSet policy - and everything is cached
// so here,
// every PerformGet/Exists just GetMany() and then filters
// except PerformGetAll which is the one really doing the job
protected override IMediaType PerformGet(int id)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Id == id);
}
=> GetMany().FirstOrDefault(x => x.Id == id);
protected override IMediaType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Key == id);
}
=> GetMany().FirstOrDefault(x => x.Key == id);
protected override bool PerformExists(Guid id)
{
return GetMany().FirstOrDefault(x => x.Key == id) != null;
}
=> GetMany().FirstOrDefault(x => x.Key == id) != null;
protected override IMediaType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
protected override IEnumerable<IMediaType> PerformGetAll(params int[] ids)
{
if (ids.Any())
{
//NOTE: This logic should never be executed according to our cache policy
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, SupportsPublishing, this)
.Where(x => ids.Contains(x.Id));
}
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, SupportsPublishing, this);
// the cache policy will always want everything
// even GetMany(ids) gets everything and filters afterwards
if (ids.Any()) throw new Exception("panic");
return CommonRepository.GetAllTypes().OfType<IMediaType>();
}
protected override IEnumerable<IMediaType> PerformGetAll(params Guid[] ids)
{
//use the underlying GetAll which will force cache all content types
if (ids.Any())
{
return GetMany().Where(x => ids.Contains(x.Key));
}
else
{
return GetMany();
}
var all = GetMany();
return ids.Any() ? all.Where(x => ids.Contains(x.Key)) : all;
}
protected override IEnumerable<IMediaType> PerformGetByQuery(IQuery<IMediaType> query)
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IMediaType>(sqlClause, query);
var baseQuery = GetBaseQuery(false);
var translator = new SqlTranslator<IMediaType>(baseQuery, query);
var sql = translator.Translate();
var ids = Database.Fetch<int>(sql).Distinct().ToArray();
var dtos = Database.Fetch<ContentTypeDto>(sql);
return
//This returns a lookup from the GetAll cached lookup
(dtos.Any()
? GetMany(dtos.DistinctBy(x => x.NodeId).Select(x => x.NodeId).ToArray())
: Enumerable.Empty<IMediaType>())
//order the result by name
.OrderBy(x => x.Name);
}
/// <summary>
/// Gets all entities of the specified <see cref="PropertyType"/> query
/// </summary>
/// <param name="query"></param>
/// <returns>An enumerable list of <see cref="IMediaType"/> objects</returns>
public IEnumerable<IMediaType> GetByQuery(IQuery<PropertyType> query)
{
var ints = PerformGetByQuery(query).ToArray();
return ints.Any()
? GetMany(ints)
: Enumerable.Empty<IMediaType>();
return ids.Length > 0 ? GetMany(ids).OrderBy(x => x.Name) : Enumerable.Empty<IMediaType>();
}
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
@@ -112,12 +75,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
sql = isCount
? sql.SelectCount()
: sql.Select<ContentTypeDto>(r => r.Select(x => x.NodeDto));
: sql.Select<ContentTypeDto>(x => x.NodeId);
sql
.From<ContentTypeDto>()
.InnerJoin<NodeDto>()
.On<ContentTypeDto, NodeDto>( left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>().On<ContentTypeDto, NodeDto>( left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
@@ -17,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
/// </summary>
internal class MemberTypeRepository : ContentTypeRepositoryBase<IMemberType>, IMemberTypeRepository
{
public MemberTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
: base(scopeAccessor, cache, logger)
public MemberTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
: base(scopeAccessor, cache, logger, commonRepository)
{ }
protected override bool SupportsPublishing => MemberType.SupportsPublishingConst;
@@ -28,108 +28,49 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return new FullDataSetRepositoryCachePolicy<IMemberType, int>(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ true);
}
// every GetExists method goes cachePolicy.GetSomething which in turns goes PerformGetAll,
// since this is a FullDataSet policy - and everything is cached
// so here,
// every PerformGet/Exists just GetMany() and then filters
// except PerformGetAll which is the one really doing the job
protected override IMemberType PerformGet(int id)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Id == id);
}
=> GetMany().FirstOrDefault(x => x.Id == id);
protected override IMemberType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Key == id);
}
=> GetMany().FirstOrDefault(x => x.Key == id);
protected override IEnumerable<IMemberType> PerformGetAll(params Guid[] ids)
{
//use the underlying GetAll which will force cache all content types
if (ids.Any())
{
return GetMany().Where(x => ids.Contains(x.Key));
}
else
{
return GetMany();
}
var all = GetMany();
return ids.Any() ? all.Where(x => ids.Contains(x.Key)) : all;
}
protected override bool PerformExists(Guid id)
{
return GetMany().FirstOrDefault(x => x.Key == id) != null;
}
=> GetMany().FirstOrDefault(x => x.Key == id) != null;
protected override IMemberType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
protected override IEnumerable<IMemberType> PerformGetAll(params int[] ids)
{
var sql = GetBaseQuery(false);
if (ids.Any())
{
//NOTE: This logic should never be executed according to our cache policy
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.id='{0}'", x)));
sql.Where(statement);
}
sql.OrderByDescending<NodeDto>(x => x.NodeId);
var dtos = Database
.Fetch<MemberTypeReadOnlyDto>(sql) // cannot use FetchOneToMany because we have 2 collections!
.Transform(MapOneToManies)
.ToList();
return BuildFromDtos(dtos);
// the cache policy will always want everything
// even GetMany(ids) gets everything and filters afterwards
if (ids.Any()) throw new Exception("panic");
return CommonRepository.GetAllTypes().OfType<IMemberType>();
}
protected override IEnumerable<IMemberType> PerformGetByQuery(IQuery<IMemberType> query)
{
var sqlSubquery = GetSubquery();
var translator = new SqlTranslator<IMemberType>(sqlSubquery, query);
var subquery = translator.Translate();
var subQuery = GetSubquery();
var translator = new SqlTranslator<IMemberType>(subQuery, query);
var subSql = translator.Translate();
var sql = GetBaseQuery(false)
.Append("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments)
.WhereIn<NodeDto>(x => x.NodeId, subSql)
.OrderBy<NodeDto>(x => x.SortOrder);
var ids = Database.Fetch<int>(sql).Distinct().ToArray();
var dtos = Database
.Fetch<MemberTypeReadOnlyDto>(sql) // cannot use FetchOneToMany because we have 2 collections!
.Transform(MapOneToManies)
.ToList();
return BuildFromDtos(dtos);
}
private IEnumerable<MemberTypeReadOnlyDto> MapOneToManies(IEnumerable<MemberTypeReadOnlyDto> dtos)
{
MemberTypeReadOnlyDto acc = null;
foreach (var dto in dtos)
{
if (acc == null)
{
acc = dto;
}
else if (acc.UniqueId == dto.UniqueId)
{
var prop = dto.PropertyTypes.SingleOrDefault();
var group = dto.PropertyTypeGroups.SingleOrDefault();
if (prop != null && prop.Id.HasValue && acc.PropertyTypes.Any(x => x.Id == prop.Id.Value) == false)
acc.PropertyTypes.Add(prop);
if (group != null && group.Id.HasValue && acc.PropertyTypeGroups.Any(x => x.Id == group.Id.Value) == false)
acc.PropertyTypeGroups.Add(group);
}
else
{
yield return acc;
acc = dto;
}
}
if (acc != null)
yield return acc;
return ids.Length > 0 ? GetMany(ids).OrderBy(x => x.Name) : Enumerable.Empty<IMemberType>();
}
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
@@ -144,18 +85,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
var sql = Sql()
.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias",
"cmsPropertyType.Name", "cmsPropertyType.Description", "cmsPropertyType.mandatory", "cmsPropertyType.UniqueID",
"cmsPropertyType.validationRegExp", "cmsPropertyType.dataTypeId", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder",
"cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId",
"cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile", "cmsMemberType.isSensitive",
$"{Constants.DatabaseSchema.Tables.DataType}.propertyEditorAlias", $"{Constants.DatabaseSchema.Tables.DataType}.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId",
"cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.uniqueID AS PropertyGroupUniqueID",
"cmsPropertyTypeGroup.sortorder AS PropertyGroupSortOrder", "cmsPropertyTypeGroup.contenttypeNodeId")
.Select<NodeDto>(x => x.NodeId)
.From<NodeDto>()
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.LeftJoin<PropertyTypeDto>().On<PropertyTypeDto, NodeDto>(left => left.ContentTypeId, right => right.NodeId)
.LeftJoin<MemberTypeDto>().On<MemberTypeDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.LeftJoin<MemberPropertyTypeDto>().On<MemberPropertyTypeDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.LeftJoin<DataTypeDto>().On<DataTypeDto, PropertyTypeDto>(left => left.NodeId, right => right.DataTypeId)
.LeftJoin<PropertyTypeGroupDto>().On<PropertyTypeGroupDto, NodeDto>(left => left.ContentTypeNodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
@@ -170,7 +104,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.From<NodeDto>()
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.LeftJoin<PropertyTypeDto>().On<PropertyTypeDto, NodeDto>(left => left.ContentTypeId, right => right.NodeId)
.LeftJoin<MemberTypeDto>().On<MemberTypeDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.LeftJoin<MemberPropertyTypeDto>().On<MemberPropertyTypeDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.LeftJoin<DataTypeDto>().On<DataTypeDto, PropertyTypeDto>(left => left.NodeId, right => right.DataTypeId)
.LeftJoin<PropertyTypeGroupDto>().On<PropertyTypeGroupDto, NodeDto>(left => left.ContentTypeNodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
@@ -212,12 +146,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
entity.AddPropertyType(standardPropertyType.Value, Constants.Conventions.Member.StandardPropertiesGroupName);
}
EnsureExplicitDataTypeForBuiltInProperties(entity);
PersistNewBaseContentType(entity);
//Handles the MemberTypeDto (cmsMemberType table)
var memberTypeDtos = ContentTypeFactory.BuildMemberTypeDtos(entity);
var memberTypeDtos = ContentTypeFactory.BuildMemberPropertyTypeDtos(entity);
foreach (var memberTypeDto in memberTypeDtos)
{
Database.Insert(memberTypeDto);
@@ -245,13 +179,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
new { ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId });
entity.SortOrder = maxSortOrder + 1;
}
EnsureExplicitDataTypeForBuiltInProperties(entity);
PersistUpdatedBaseContentType(entity);
// remove and insert - handle cmsMemberType table
Database.Delete<MemberTypeDto>("WHERE NodeId = @Id", new { Id = entity.Id });
var memberTypeDtos = ContentTypeFactory.BuildMemberTypeDtos(entity);
Database.Delete<MemberPropertyTypeDto>("WHERE NodeId = @Id", new { Id = entity.Id });
var memberTypeDtos = ContentTypeFactory.BuildMemberPropertyTypeDtos(entity);
foreach (var memberTypeDto in memberTypeDtos)
{
Database.Insert(memberTypeDto);
@@ -264,19 +198,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
/// Override so we can specify explicit db type's on any property types that are built-in.
/// </summary>
/// <param name="propertyEditorAlias"></param>
/// <param name="dbType"></param>
/// <param name="storageType"></param>
/// <param name="propertyTypeAlias"></param>
/// <returns></returns>
protected override PropertyType CreatePropertyType(string propertyEditorAlias, ValueStorageType dbType, string propertyTypeAlias)
protected override PropertyType CreatePropertyType(string propertyEditorAlias, ValueStorageType storageType, string propertyTypeAlias)
{
//custom property type constructor logic to set explicit dbtype's for built in properties
var stdProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
var propDbType = GetDbTypeForBuiltInProperty(propertyTypeAlias, dbType, stdProps);
return new PropertyType(propertyEditorAlias, propDbType.Result,
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
// which is what we want for the built-in properties.
propDbType.Success,
propertyTypeAlias);
var builtinProperties = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
var readonlyStorageType = builtinProperties.TryGetValue(propertyTypeAlias, out var propertyType);
storageType = readonlyStorageType ? propertyType.ValueStorageType : storageType;
return new PropertyType(propertyEditorAlias, storageType, readonlyStorageType, propertyTypeAlias);
}
/// <summary>
@@ -286,62 +217,15 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
/// <param name="memberType"></param>
private static void EnsureExplicitDataTypeForBuiltInProperties(IContentTypeBase memberType)
{
var stdProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
var builtinProperties = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
foreach (var propertyType in memberType.PropertyTypes)
{
var dbTypeAttempt = GetDbTypeForBuiltInProperty(propertyType.Alias, propertyType.ValueStorageType, stdProps);
if (dbTypeAttempt)
if (builtinProperties.ContainsKey(propertyType.Alias))
{
//this reset's it's current data type reference which will be re-assigned based on the property editor assigned on the next line
//this reset's its current data type reference which will be re-assigned based on the property editor assigned on the next line
propertyType.DataTypeId = 0;
}
}
}
/// <summary>
/// Builds a collection of entities from a collection of Dtos
/// </summary>
/// <param name="dtos"></param>
/// <returns></returns>
private IEnumerable<IMemberType> BuildFromDtos(List<MemberTypeReadOnlyDto> dtos)
{
if (dtos == null || dtos.Any() == false)
return Enumerable.Empty<IMemberType>();
return dtos.Select(x =>
{
bool needsSaving;
var memberType = MemberTypeReadOnlyFactory.BuildEntity(x, out needsSaving);
if (needsSaving) PersistUpdatedItem(memberType);
return memberType;
}).ToList();
}
/// <summary>
/// If this is one of our internal properties - we will manually assign the data type since they must
/// always correspond to the correct db type no matter what the backing data type is assigned.
/// </summary>
/// <param name="propAlias"></param>
/// <param name="dbType"></param>
/// <param name="standardProps"></param>
/// <returns>
/// Successful attempt if it was a built in property
/// </returns>
internal static Attempt<ValueStorageType> GetDbTypeForBuiltInProperty(
string propAlias,
ValueStorageType dbType,
Dictionary<string, PropertyType> standardProps)
{
var aliases = standardProps.Select(x => x.Key).ToArray();
//check if it is built in
if (aliases.Contains(propAlias))
{
//return the pre-determined db type for this property
return Attempt<ValueStorageType>.Succeed(standardProps.Single(x => x.Key == propAlias).Value.ValueStorageType);
}
return Attempt<ValueStorageType>.Fail(dbType);
}
}
}
@@ -78,51 +78,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
}
// TODO: but now that we have 1 unique repository?
// this is a *bad* idea because PerformCount captures the current repository and its UOW
//
//private static RepositoryCachePolicyOptions _defaultOptions;
//protected virtual RepositoryCachePolicyOptions DefaultOptions
//{
// get
// {
// return _defaultOptions ?? (_defaultOptions
// = new RepositoryCachePolicyOptions(() =>
// {
// // get count of all entities of current type (TEntity) to ensure cached result is correct
// // create query once if it is needed (no need for locking here) - query is static!
// var query = _hasIdQuery ?? (_hasIdQuery = Query<TEntity>.Builder.Where(x => x.Id != 0));
// return PerformCount(query);
// }));
// }
//}
// ReSharper disable once StaticMemberInGenericType
private static RepositoryCachePolicyOptions _defaultOptions;
// ReSharper disable once InconsistentNaming
protected virtual RepositoryCachePolicyOptions DefaultOptions
{
get
{
return new RepositoryCachePolicyOptions(() =>
{
// get count of all entities of current type (TEntity) to ensure cached result is correct
// create query once if it is needed (no need for locking here) - query is static!
var query = _hasIdQuery ?? (_hasIdQuery = AmbientScope.SqlContext.Query<TEntity>().Where(x => x.Id != 0));
return PerformCount(query);
});
return _defaultOptions ?? (_defaultOptions
= new RepositoryCachePolicyOptions(() =>
{
// get count of all entities of current type (TEntity) to ensure cached result is correct
// create query once if it is needed (no need for locking here) - query is static!
var query = _hasIdQuery ?? (_hasIdQuery = AmbientScope.SqlContext.Query<TEntity>().Where(x => x.Id != 0));
return PerformCount(query);
}));
}
}
// this would be better for perfs BUT it breaks the tests - l8tr
//
//private static IRepositoryCachePolicy<TEntity, TId> _defaultCachePolicy;
//protected virtual IRepositoryCachePolicy<TEntity, TId> DefaultCachePolicy
//{
// get
// {
// return _defaultCachePolicy ?? (_defaultCachePolicy
// = new DefaultRepositoryCachePolicy<TEntity, TId>(IsolatedCache, DefaultOptions));
// }
//}
protected IRepositoryCachePolicy<TEntity, TId> CachePolicy
{
get
@@ -822,7 +822,7 @@ ORDER BY colName";
var expressionMember = ExpressionHelper.GetMemberInfo(orderBy);
var mapper = _mapperCollection[typeof(IUser)];
var mappedField = mapper.Map(SqlContext.SqlSyntax, expressionMember.Name);
var mappedField = mapper.Map(expressionMember.Name);
if (mappedField.IsNullOrWhiteSpace())
throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause");
@@ -1,28 +1,12 @@
using System.Collections.Generic;
using AutoMapper;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
namespace Umbraco.Core.Runtime
{
public class CoreInitialComponent : IComponent
{
private readonly IEnumerable<Profile> _mapperProfiles;
public CoreInitialComponent(IEnumerable<Profile> mapperProfiles)
{
_mapperProfiles = mapperProfiles;
}
public void Initialize()
{
// mapper profiles have been registered & are created by the container
Mapper.Initialize(configuration =>
{
foreach (var profile in _mapperProfiles)
configuration.AddProfile(profile);
});
// ensure we have some essential directories
// every other component can then initialize safely
IOHelper.EnsurePathExists("~/App_Data");
+7 -21
View File
@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
@@ -12,10 +10,8 @@ using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Logging.Serilog;
using Umbraco.Core.Migrations.Upgrade;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Services.Implement;
using Umbraco.Core.Sync;
namespace Umbraco.Core.Runtime
@@ -70,15 +66,19 @@ namespace Umbraco.Core.Runtime
// objects.
using (var timer = profilingLogger.TraceDuration<CoreRuntime>(
$"Booting Umbraco {UmbracoVersion.SemanticVersion.ToSemanticString()} on {NetworkHelper.MachineName}.",
$"Booting Umbraco {UmbracoVersion.SemanticVersion.ToSemanticString()}.",
"Booted.",
"Boot failed."))
{
logger.Info<CoreRuntime>("Booting site '{HostingSiteName}', app '{HostingApplicationID}', path '{HostingPhysicalPath}', server '{MachineName}'.",
HostingEnvironment.SiteName,
HostingEnvironment.ApplicationID,
HostingEnvironment.ApplicationPhysicalPath,
NetworkHelper.MachineName);
logger.Debug<CoreRuntime>("Runtime: {Runtime}", GetType().FullName);
// application environment
ConfigureUnhandledException();
ConfigureAssemblyResolve();
ConfigureApplicationRootPath();
Boot(register, timer);
@@ -211,20 +211,6 @@ namespace Umbraco.Core.Runtime
};
}
protected virtual void ConfigureAssemblyResolve()
{
// When an assembly can't be resolved. In here we can do magic with the assembly name and try loading another.
// This is used for loading a signed assembly of AutoMapper (v. 3.1+) without having to recompile old code.
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stack overflow
if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null"))
return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005"));
return null;
};
}
protected virtual void ConfigureApplicationRootPath()
{
var path = GetApplicationRootPath();
+1 -1
View File
@@ -252,7 +252,7 @@ namespace Umbraco.Core
FinalMigrationState = upgrader.Plan.FinalState;
}
logger.Debug<RuntimeState>("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", CurrentMigrationState, FinalMigrationState ?? "<null>");
logger.Debug<RuntimeState>("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", FinalMigrationState, CurrentMigrationState ?? "<null>");
return CurrentMigrationState == FinalMigrationState;
}
@@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Security;
using AutoMapper;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
@@ -19,7 +17,7 @@ using Task = System.Threading.Tasks.Task;
namespace Umbraco.Core.Security
{
public class BackOfficeUserStore : DisposableObjectSlim,
public class BackOfficeUserStore : DisposableObjectSlim,
IUserStore<BackOfficeIdentityUser, int>,
IUserPasswordStore<BackOfficeIdentityUser, int>,
IUserEmailStore<BackOfficeIdentityUser, int>,
@@ -40,9 +38,10 @@ namespace Umbraco.Core.Security
private readonly IEntityService _entityService;
private readonly IExternalLoginService _externalLoginService;
private readonly IGlobalSettings _globalSettings;
private readonly UmbracoMapper _mapper;
private bool _disposed = false;
public BackOfficeUserStore(IUserService userService, IMemberTypeService memberTypeService, IEntityService entityService, IExternalLoginService externalLoginService, IGlobalSettings globalSettings, MembershipProviderBase usersMembershipProvider)
public BackOfficeUserStore(IUserService userService, IMemberTypeService memberTypeService, IEntityService entityService, IExternalLoginService externalLoginService, IGlobalSettings globalSettings, MembershipProviderBase usersMembershipProvider, UmbracoMapper mapper)
{
_userService = userService;
_memberTypeService = memberTypeService;
@@ -52,6 +51,7 @@ namespace Umbraco.Core.Security
if (userService == null) throw new ArgumentNullException("userService");
if (usersMembershipProvider == null) throw new ArgumentNullException("usersMembershipProvider");
if (externalLoginService == null) throw new ArgumentNullException("externalLoginService");
_mapper = mapper;
_userService = userService;
_externalLoginService = externalLoginService;
@@ -185,7 +185,8 @@ namespace Umbraco.Core.Security
{
return null;
}
return await Task.FromResult(AssignLoginsCallback(Mapper.Map<BackOfficeIdentityUser>(user)));
return await Task.FromResult(AssignLoginsCallback(_mapper.Map<BackOfficeIdentityUser>(user)));
}
/// <summary>
@@ -202,7 +203,7 @@ namespace Umbraco.Core.Security
return null;
}
var result = AssignLoginsCallback(Mapper.Map<BackOfficeIdentityUser>(user));
var result = AssignLoginsCallback(_mapper.Map<BackOfficeIdentityUser>(user));
return await Task.FromResult(result);
}
@@ -314,7 +315,7 @@ namespace Umbraco.Core.Security
var user = _userService.GetByEmail(email);
var result = user == null
? null
: Mapper.Map<BackOfficeIdentityUser>(user);
: _mapper.Map<BackOfficeIdentityUser>(user);
return Task.FromResult(AssignLoginsCallback(result));
}
@@ -391,7 +392,7 @@ namespace Umbraco.Core.Security
var user = _userService.GetUserById(l.UserId);
if (user != null)
{
output = Mapper.Map<BackOfficeIdentityUser>(user);
output = _mapper.Map<BackOfficeIdentityUser>(user);
break;
}
}
@@ -10,7 +10,7 @@ using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services.Changes;
@@ -505,7 +505,7 @@ namespace Umbraco.Core.Services.Implement
/// <returns></returns>
public IEnumerable<int> GetVersionIds(int id, int maxRows)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
using (ScopeProvider.CreateScope(autoComplete: true))
{
return _documentRepository.GetVersionIds(id, maxRows);
}
@@ -533,7 +533,7 @@ namespace Umbraco.Core.Services.Implement
//null check otherwise we get exceptions
if (content.Path.IsNullOrWhiteSpace()) return Enumerable.Empty<IContent>();
var rootId = Constants.System.Root.ToInvariantString();
var rootId = Constants.System.RootString;
var ids = content.Path.Split(',')
.Where(x => x != rootId && x != content.Id.ToString(CultureInfo.InvariantCulture)).Select(int.Parse).ToArray();
if (ids.Any() == false)
@@ -877,38 +877,15 @@ namespace Umbraco.Core.Services.Implement
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
Property[] invalidProperties;
// if culture is specific, first publish the invariant values, then publish the culture itself.
// if culture is '*', then publish them all (including variants)
// explicitly SaveAndPublish a specific culture also publishes invariant values
if (!culture.IsNullOrWhiteSpace() && culture != "*")
{
// publish the invariant values
var publishInvariant = content.PublishCulture(null);
if (!publishInvariant)
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content);
//validate the property values
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out invalidProperties))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content)
{
InvalidProperties = invalidProperties
};
}
//this will create the correct culture impact even if culture is * or null
var impact = CultureImpact.Create(culture, _languageRepository.IsDefault(culture), content);
// publish the culture(s)
var publishCulture = content.PublishCulture(culture);
if (!publishCulture)
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content);
//validate the property values
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out invalidProperties))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content)
{
InvalidProperties = invalidProperties
};
// we don't care about the response here, this response will be rechecked below but we need to set the culture info values now.
content.PublishCulture(impact);
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
scope.Complete();
@@ -941,16 +918,15 @@ namespace Umbraco.Core.Services.Implement
: new PublishResult(PublishResultType.FailedPublishNothingToPublish, evtMsgs, content);
}
if (cultures.Any(x => x == null || x == "*"))
throw new InvalidOperationException("Only valid cultures are allowed to be used in this method, wildcards or nulls are not allowed");
if (cultures.Select(content.PublishCulture).Any(isValid => !isValid))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content);
var impacts = cultures.Select(x => CultureImpact.Explicit(x, _languageRepository.IsDefault(x)));
//validate the property values
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out var invalidProperties))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content)
{
InvalidProperties = invalidProperties
};
// publish the culture(s)
// we don't care about the response here, this response will be rechecked below but we need to set the culture info values now.
foreach (var impact in impacts)
content.PublishCulture(impact);
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
scope.Complete();
@@ -1096,6 +1072,7 @@ namespace Umbraco.Core.Services.Implement
if (publishing)
{
//determine cultures publishing/unpublishing which will be based on previous calls to content.PublishCulture and ClearPublishInfo
culturesUnpublishing = content.GetCulturesUnpublishing();
culturesPublishing = variesByCulture
? content.PublishCultureInfos.Values.Where(x => x.IsDirty()).Select(x => x.Culture).ToList()
@@ -1147,7 +1124,7 @@ namespace Umbraco.Core.Services.Implement
// note: This unpublishes the entire document (not different variants)
unpublishResult = StrategyCanUnpublish(scope, content, evtMsgs);
if (unpublishResult.Success)
unpublishResult = StrategyUnpublish(scope, content, userId, evtMsgs);
unpublishResult = StrategyUnpublish(content, evtMsgs);
else
{
// reset published state from temp values (publishing, unpublishing) to original value
@@ -1330,7 +1307,8 @@ namespace Umbraco.Core.Services.Implement
//publish the culture values and validate the property values, if validation fails, log the invalid properties so the develeper has an idea of what has failed
Property[] invalidProperties = null;
var tryPublish = d.PublishCulture(culture) && _propertyValidationService.Value.IsPropertyDataValid(d, out invalidProperties);
var impact = CultureImpact.Explicit(culture, _languageRepository.IsDefault(culture));
var tryPublish = d.PublishCulture(impact) && _propertyValidationService.Value.IsPropertyDataValid(d, out invalidProperties, impact);
if (invalidProperties != null && invalidProperties.Length > 0)
Logger.Warn<ContentService>("Scheduled publishing will fail for document {DocumentId} and culture {Culture} because of invalid properties {InvalidProperties}",
d.Id, culture, string.Join(",", invalidProperties.Select(x => x.Alias)));
@@ -1426,9 +1404,17 @@ namespace Umbraco.Core.Services.Implement
// variant content type - publish specified cultures
// invariant content type - publish only the invariant culture
return content.ContentType.VariesByCulture()
? culturesToPublish.All(culture => content.PublishCulture(culture) && _propertyValidationService.Value.IsPropertyDataValid(content, out _))
: content.PublishCulture() && _propertyValidationService.Value.IsPropertyDataValid(content, out _);
if (content.ContentType.VariesByCulture())
{
return culturesToPublish.All(culture =>
{
var impact = CultureImpact.Create(culture, _languageRepository.IsDefault(culture), content);
return content.PublishCulture(impact) && _propertyValidationService.Value.IsPropertyDataValid(content, out _, impact);
});
}
return content.PublishCulture(CultureImpact.Invariant)
&& _propertyValidationService.Value.IsPropertyDataValid(content, out _, CultureImpact.Invariant);
}
// utility 'ShouldPublish' func used by SaveAndPublishBranch
@@ -1922,7 +1908,7 @@ namespace Umbraco.Core.Services.Implement
// if uow is not immediate, content.Path will be updated only when the UOW commits,
// and because we want it now, we have to calculate it by ourselves
//paths[content.Id] = content.Path;
paths[content.Id] = (parent == null ? (parentId == Constants.System.RecycleBinContent ? "-1,-20" : "-1") : parent.Path) + "," + content.Id;
paths[content.Id] = (parent == null ? (parentId == Constants.System.RecycleBinContent ? "-1,-20" : Constants.System.RootString) : parent.Path) + "," + content.Id;
const int pageSize = 500;
var page = 0;
@@ -2500,10 +2486,29 @@ namespace Umbraco.Core.Services.Implement
var variesByCulture = content.ContentType.VariesByCulture();
//First check if mandatory languages fails, if this fails it will mean anything that the published flag on the document will
var impactsToPublish = culturesPublishing == null
? new[] {CultureImpact.Invariant} //if it's null it's invariant
: culturesPublishing.Select(x => CultureImpact.Explicit(x, _languageRepository.IsDefault(x))).ToArray();
// publish the culture(s)
if (!impactsToPublish.All(content.PublishCulture))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content);
//validate the property values
Property[] invalidProperties = null;
if (!impactsToPublish.All(x => _propertyValidationService.Value.IsPropertyDataValid(content, out invalidProperties, x)))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content)
{
InvalidProperties = invalidProperties
};
//Check if mandatory languages fails, if this fails it will mean anything that the published flag on the document will
// be changed to Unpublished and any culture currently published will not be visible.
if (variesByCulture)
{
if (culturesPublishing == null)
throw new InvalidOperationException("Internal error, variesByCulture but culturesPublishing is null.");
if (content.Published && culturesPublishing.Count == 0 && culturesUnpublishing.Count == 0) // no published cultures = cannot be published
return new PublishResult(PublishResultType.FailedPublishNothingToPublish, evtMsgs, content);
@@ -2639,15 +2644,13 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Unpublishes a document
/// </summary>
/// <param name="scope"></param>
/// <param name="content"></param>
/// <param name="userId"></param>
/// <param name="evtMsgs"></param>
/// <returns></returns>
/// <remarks>
/// It is assumed that all unpublishing checks have passed before calling this method like <see cref="StrategyCanUnpublish"/>
/// </remarks>
private PublishResult StrategyUnpublish(IScope scope, IContent content, int userId, EventMessages evtMsgs)
private PublishResult StrategyUnpublish(IContent content, EventMessages evtMsgs)
{
var attempt = new PublishResult(PublishResultType.SuccessUnpublish, evtMsgs, content);
@@ -2899,7 +2902,7 @@ namespace Umbraco.Core.Services.Implement
public IEnumerable<IContent> GetBlueprintsForContentTypes(params int[] contentTypeId)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
using (ScopeProvider.CreateScope(autoComplete: true))
{
var query = Query<IContent>();
if (contentTypeId.Length > 0)
@@ -424,7 +424,6 @@ namespace Umbraco.Core.Services.Implement
return;
}
// find ContentTypes using this IDataTypeDefinition on a PropertyType, and delete
// TODO: media and members?!
// TODO: non-group properties?!

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