Compare commits

...

688 Commits

Author SHA1 Message Date
Claus d7d6b7f128 Merge pull request #2116 from umbraco/temp-deploy-350
changes needed for blueprints to differentiate them from content items.
2017-08-10 08:37:26 +02:00
Shannon 62e467d860 Merge branch 'temp-U4-10272' into dev-v7.7 2017-08-10 14:27:47 +10:00
Shannon 021010d7ea Merge branch 'dev-v7.7' into temp-U4-10272
# Conflicts:
#	src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs
2017-08-10 14:27:37 +10:00
Shannon 4a21675a0a removes null check on email since this will cause the log to fill up, we already check for null below anyways. 2017-08-09 23:42:31 +10:00
Claus 73bc5c6ec9 changes needed for blueprints to differentiate them from content items. 2017-08-09 15:29:12 +02:00
Shannon 98a545bdc6 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7
# Conflicts:
#	src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
2017-08-09 23:18:51 +10:00
Shannon c27e9e9c77 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7 2017-08-09 19:45:58 +10:00
Shannon Deminick cbf5e25e1f Merge pull request #2112 from umbraco/temp-U4-10271
U4-10271 Adds contextual err message for GetValue
2017-08-09 19:40:35 +10:00
Shannon Deminick 41e555881f Merge pull request #2100 from umbraco/temp-U4-10059
U4-10059 Localize users and user groups
2017-08-09 19:35:40 +10:00
Warren Buckley b60c700e05 Updated to return nulls as opposed to the ex being caught with a meaningful message 2017-08-09 10:23:55 +01:00
Mads Rasmussen 60ffd20e20 Merge branch 'dev-v7.7' into temp-U4-10059 2017-08-09 11:01:05 +02:00
Jeavon fb4af74916 Fix so that health check notifications without config don't cause all notification methods to fail 2017-08-08 16:50:17 +01:00
Warren Buckley 8b962cff0c Catches a dictionary key not found exception & gives better error message with context 2017-08-08 16:07:16 +01:00
Mads Rasmussen fc8c750fb0 fixes permissions click area - only show pointer cursor where you can click 2017-08-08 10:22:44 +02:00
Shannon Deminick 4d7fa64a6c Merge pull request #2108 from umbraco/temp-U4-10257
U4-10257 Add "invite user" success state
2017-08-08 13:20:42 +10:00
Shannon bbe8271c35 Fixes invite user and setting the model correctly. Fixes server side validation to ensure any created user MUST have a group assigned. 2017-08-08 13:20:21 +10:00
Shannon 2d42a6ca16 Merge branch 'dev-v7.7' into temp-U4-10257
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html
2017-08-08 13:02:57 +10:00
Shannon Deminick 4879215146 Merge pull request #2104 from umbraco/temp-U4-10236
fixes: U4-10236 Umbraco.7.7 beta - add larger click area of permissio…
2017-08-08 12:57:13 +10:00
Shannon Deminick 48a6929077 Merge pull request #2106 from umbraco/temp-U4-10235
fixes: U4-10235 Umbraco 7.7 beta - misleading placeholder text in pro…
2017-08-08 12:52:12 +10:00
Shannon Deminick efc52e26e5 Merge pull request #2105 from umbraco/temp-U4-10230
fixes: U4-10230 Umbraco 7.7 beta - hover on table row affect datepicker
2017-08-08 12:49:35 +10:00
Shannon 1258148e37 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7
# Conflicts:
#	src/Umbraco.Web.UI/umbraco/config/lang/pl.xml
2017-08-08 12:36:16 +10:00
Shannon 2c7f0c54bb Updates unit tests to show usages of UDIs for pickers instead of INT 2017-08-08 12:35:12 +10:00
Mads Rasmussen 405d8e8253 fix line indention 2017-08-07 21:32:19 +02:00
Mads Rasmussen b335ca0a10 change input type to email 2017-08-07 21:19:30 +02:00
Mads Rasmussen 6658ecf6e3 add success state for "invite user" 2017-08-07 15:24:19 +02:00
Mads Rasmussen cfee55d0db fix line indention 2017-08-07 14:35:54 +02:00
Mads Rasmussen 80d49f9bc8 fix line indention 2017-08-07 14:33:55 +02:00
Mads Rasmussen 6efc6dfe6a fixes: U4-10235 Umbraco 7.7 beta - misleading placeholder text in profile email 2017-08-07 13:45:42 +02:00
Mads Rasmussen 51624cee57 Merge pull request #2096 from skttl/patch-8
U4-10231 Avatar is slightly off center
2017-08-07 13:38:02 +02:00
Mads Rasmussen 701082a909 fixes: U4-10230 Umbraco 7.7 beta - hover on table row affect datepicker 2017-08-07 13:20:01 +02:00
Mads Rasmussen 81b2e4e050 fixes: U4-10236 Umbraco.7.7 beta - add larger click area of permission items 2017-08-07 12:27:21 +02:00
Mads Rasmussen be9fbf14f1 Merge pull request #2095 from skttl/patch-7
U4-10228: Overflowing text in group dropdown in permissions menu
2017-08-07 12:08:58 +02:00
Shannon e31ec37c9e Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7 2017-08-07 17:17:38 +10:00
Mads Rasmussen 3ba3d8977f don't show start node data in the content permission dialog 2017-08-04 11:23:36 +02:00
Mads Rasmussen 19af28dd93 localize content permissions dialog 2017-08-04 11:21:43 +02:00
Mads Rasmussen 91cda667a2 U4-10221 When creating content that has Content Templates (blueprints) created for it, we should distinguish between "Blank' and Blueprints - swap class to one with no padding 2017-08-03 14:56:11 +02:00
mikkelhm 1c59c0b164 Dont output the Healthcheck.Release.config file when publishing the webapp, i.e. for nu nuget package 2017-08-02 23:57:37 +02:00
Søren Kottal 9fc87a3986 Changes styling for avatar
A div has been introduced, and is aligning the avatar to the left of the 40x40 block.

This moves the sizing and border-radius to the surrounding div, and adds a margin to center it.
2017-08-02 20:31:35 +02:00
Søren Kottal 3a44f70cee Removes inline width on dropdown for groups 2017-08-02 19:55:05 +02:00
Stephan 74b9a43685 Blueprint fixes 2017-08-02 17:43:39 +02:00
Shannon 23e16a9182 Merge branch 'dev-v7.7' of https://github.com/umbraco/Umbraco-CMS into dev-v7.7 2017-08-03 01:27:38 +10:00
Shannon a3cf41284b fixes test 2017-08-03 01:27:13 +10:00
Stephan 2907b0dd1a Blueprint fixes 2017-08-02 17:14:45 +02:00
Sebastiaan Janssen c89f44bb3d Let's aim for only 1 beta ;) 2017-08-02 16:52:08 +02:00
Jeavon 3a13cd82ed Fix customerrorscheck success message as it expect only 1 token not 2 2017-08-02 15:35:53 +01:00
Stephan 90e6815164 Blueprint fixes 2017-08-02 16:11:54 +02:00
Shannon Deminick f59bd481f4 Merge pull request #1983 from umbraco/temp-U4-9984
U4-9984 - Adds option to run Health Checks on a schedule and provide options for notifications
2017-08-02 23:58:16 +10:00
Shannon d459246519 oops 2017-08-02 23:41:31 +10:00
Shannon efbfef74c2 localizes email, fixes issues with smtp and async 2017-08-02 23:39:31 +10:00
Stephan 1ee131db81 IContent.IsBlueprint required for blueprints 2017-08-02 15:02:34 +02:00
Shannon cb9e631d7b changes get/set to private set, should not be mutable 2017-08-02 22:38:55 +10:00
Shannon 9ced53c9a9 Merge branch 'temp-U4-9984' of https://github.com/umbraco/Umbraco-CMS into temp-U4-9984 2017-08-02 22:32:54 +10:00
Shannon df287f291e Updates health check config to work the Umbraco/Testable way 2017-08-02 22:32:28 +10:00
Jeavon fc861fecbc Make the setters in NotifcationMethodBase readonly and make AllChecksSuccessful public 2017-08-02 12:54:37 +01:00
Jeavon 6aeb1a8845 Adjust all HealthCheck constants to correct naming convention 2017-08-02 12:26:35 +01:00
Jeavon 8c493a1ff9 Adding interfaces for all HealthCheck configuration to allow for testing 2017-08-02 12:22:51 +01:00
Sebastiaan Janssen 9b7b0d8923 Merge pull request #2092 from umbraco/temp-machine-key-install
U4-10222 Install a custom machine key during umbraco installation
2017-08-02 12:32:12 +02:00
Jeavon 51f51c1762 Revert this csproj change, not sure when VS added it 2017-08-02 11:29:31 +01:00
Jeavon b6e377a560 Removing further NuGet packages that were added by the Slack package 2017-08-02 11:25:28 +01:00
Shannon Deminick 4534581b29 Merge pull request #2093 from umbraco/temp-U4-10119
fixes: U4-10119 ISearchableTree UI updates
2017-08-02 20:21:46 +10:00
Jeavon b915f723f5 Removing Slack Health Check notification 2017-08-02 11:20:00 +01:00
Mads Rasmussen 36768a19c1 fixes: U4-10119 ISearchableTree UI updates 2017-08-02 12:18:06 +02:00
Jeavon bf5a48c5f5 Merge branch 'dev-v7.7' into temp-U4-9984 2017-08-02 10:58:53 +01:00
Shannon 040aae2429 fixes upgrade script 2017-08-02 19:57:14 +10:00
Jeavon c7f6416a0b Fix the merge 2017-08-02 10:53:50 +01:00
Jeavon 8259024e20 Merge branch 'dev-v7.7' into temp-U4-9984
# Conflicts:
#	src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs
2017-08-02 10:48:43 +01:00
Shannon 31ea03a7cc adds notes 2017-08-02 17:06:57 +10:00
Shannon 150e2b12e8 adding back the functionality to install a machine key during installation 2017-08-02 16:16:59 +10:00
Shannon e29ac4d17c Fixes issue with the change made to WebSecurity.ValidateBackOfficeCredentials ... not sure what i was thinking 2017-08-02 16:04:37 +10:00
Shannon Deminick ae30245a9b Merge pull request #2089 from umbraco/temp-77-upgrade-sqlce
The try catch for getting a user when we are in Upgrading state also …
2017-08-02 15:31:36 +10:00
Shannon 595e26e41f U4-10221 When creating content that has Content Templates (blueprints) created for it, we should distinguish between "Blank' and Blueprints 2017-08-02 15:30:17 +10:00
Shannon dda19e292a U4-10217 Blueprints: When clicking "Create Blueprint", add explanation on what it is and what will happen 2017-08-02 13:28:50 +10:00
Shannon 08f07ff3b7 Moves blueprint localization copy into it's own section in the lang files 2017-08-02 12:58:31 +10:00
Shannon Deminick e9e8d0d4b1 Merge pull request #2090 from umbraco/temp-blueprint-rename
Implements U4-10219 and U4-10218
2017-08-02 12:30:12 +10:00
Shannon 9d3a1fe529 updates design so it works and some wording for blueprints intro 2017-08-02 12:26:33 +10:00
Shannon 4f396222dc Merge branch 'dev-v7.7' into temp-blueprint-rename 2017-08-02 12:13:04 +10:00
Shannon 68f5a141c5 ensure when the user upgrades that the admin and editor have blueprint permissions 2017-08-02 12:11:42 +10:00
Shannon Deminick 9c0ca85c41 Merge pull request #2091 from umbraco/temp-U4-10202
U4-10202 Make it more obvious when no content or media start nodes ar…
2017-08-02 12:10:09 +10:00
Shannon 5cd314335c formatting 2017-08-02 11:56:11 +10:00
Shannon 241d486e97 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7
# Conflicts:
#	src/Umbraco.Web/Security/Identity/FormsAuthenticationSecureDataFormat.cs
2017-08-02 11:55:15 +10:00
Mads Rasmussen b0f1b19385 allow current user to see but not edit its access rights 2017-08-01 20:06:19 +02:00
Mads Rasmussen b6de92167b remove "lorem ipsum" + fix title on node permissions overlay 2017-08-01 19:40:11 +02:00
Niels Hartvig 7e49cc0f25 Implements U4-10219 and U4-10218 2017-08-01 16:19:32 +02:00
Mads Rasmussen 1c5bcbc5bb U4-10202 Make it more obvious when no content or media start nodes are selected the user has access to the root node. 2017-08-01 16:18:57 +02:00
Stephan 5fab5102d9 Fix UmbracoObjectTypes enum bkwd compatibility issue 2017-08-01 15:51:23 +02:00
mikkelhm 6655be5f5e Updates the trees.config.install.xdt to update the tree.config with the new User section 2017-08-01 14:24:04 +02:00
Mads Rasmussen 60cd82884f add localizationService as dependency to userGroupPicker controller 2017-08-01 14:13:02 +02:00
mikkelhm eb93c0de11 Removes c#6 exception filter syntax 2017-08-01 12:33:57 +02:00
mikkelhm 8cb29182f4 The try catch for getting a user when we are in Upgrading state also needs to work for SqlCe 2017-08-01 12:19:26 +02:00
Mads Rasmussen 0786d0849c fix js errors in user controller after merge 2017-08-01 10:34:19 +02:00
mikkelhm 005bee7650 Updates web.config.install.xdt to have the latest assemblyBindings for the Katana libs(Owin), v 3.1.0.0 2017-08-01 10:29:22 +02:00
mikkelhm 39ce2067ef Removes references to deleted uninstall files, uninstall.core.ps1, uninstall.ps1 and web.config.uninstall.xdt 2017-08-01 10:22:17 +02:00
Shannon 59db5b0081 Obsoletes more dynamic published content extension methods and models 2017-08-01 17:04:16 +10:00
Shannon 44b90e2ce6 removes old template editor webforms files 2017-08-01 17:03:33 +10:00
Shannon 6829660720 removes old scripting templates 2017-08-01 17:02:45 +10:00
Shannon 67a69d2e11 bumps version, fixes install with setting built in group start nodes 2017-08-01 16:21:11 +10:00
Shannon 80364fc4d2 Fixes last unit test ( i think ) 2017-08-01 12:58:18 +10:00
Shannon Deminick e02c6591d9 Merge pull request #2080 from umbraco/temp-U4-10202
fixes: U4-10202 Make it more obvious when no content or media start n…
2017-08-01 12:42:26 +10:00
Shannon 2e6fb8e3e5 Fixes merge issues, ensures "Content root" and "Media root" are localized 2017-08-01 12:41:22 +10:00
Shannon cee3f456d1 Merge branch 'dev-v7.7' into temp-U4-10202
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-group-preview.html
#	src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
2017-08-01 12:40:47 +10:00
Shannon Deminick 46bb0cca2d Merge pull request #2084 from umbraco/temp-U4-10059
U4-10059 Localize users and user groups
2017-08-01 12:09:32 +10:00
Shannon 2beba8067e updates the wording of a few items and updates the en.xml lang file too 2017-08-01 12:02:35 +10:00
Shannon 175da06d60 Merge branch 'dev-v7.7' into temp-U4-10059
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/users/group.html
#	src/Umbraco.Web.UI.Client/src/views/users/user.html
#	src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
2017-08-01 11:31:52 +10:00
Shannon 7769a0ba42 Updates logic to show if the user or group has not start nodes defined (which does not anymore indicate root access) 2017-08-01 11:19:31 +10:00
Shannon fd65380367 Merge branch 'dev-v7.7' into temp-U4-10202
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/users/user.html
2017-08-01 10:35:37 +10:00
Shannon Deminick 9880e9b6a5 Merge pull request #2086 from umbraco/temp-U4-10212
fixes: U4-10212 Clean up test css classes
2017-08-01 10:32:53 +10:00
Shannon Deminick 134033bc3d Merge pull request #2085 from umbraco/temp-U4-10210
fixes: U4-10210 Add title and filter to user groups overview
2017-08-01 10:31:05 +10:00
Shannon Deminick 8bf3835212 Merge pull request #2070 from umbraco/temp-U4-10155
fixes: U4-10155 Prompt the user before deleting user groups in the us…
2017-08-01 10:16:02 +10:00
Shannon 6782e71841 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7 2017-08-01 09:41:21 +10:00
Shannon Deminick 7b449b4bad Merge pull request #2079 from umbraco/temp-U4-10095
U4-10095 Need to be able to remove a user's avatar
2017-08-01 09:40:47 +10:00
Mads Rasmussen c539db39ec fixes: U4-10212 Clean up test css classes 2017-07-31 14:58:17 +02:00
Mads Rasmussen 2cc881883b fixes: U4-10210 Add title and filter to user groups overview 2017-07-31 13:20:33 +02:00
Shannon bcf4a84725 fixes more tests, gotta be close now 2017-07-31 19:47:02 +10:00
Mads Rasmussen 164f1e253e only show "remove photo" button if the user has an avatar 2017-07-31 11:39:59 +02:00
Mads Rasmussen 5c0af42d00 add descriptions to permissions 2017-07-31 11:24:11 +02:00
Mads Rasmussen 38ae9d6d52 update lorem ipsum texts 2017-07-31 10:52:45 +02:00
Shannon b8971f0dd1 decouple user repo from the membershi providers, lets see what happens with unit tests on the server 2017-07-31 18:15:30 +10:00
Mads Rasmussen 939d429b8b localise user group picker and user group controller 2017-07-31 09:23:20 +02:00
Shannon d103151159 fixing tests 2017-07-31 17:11:12 +10:00
Shannon 796708075c Fixes JS errors when loading in a textbox editor when there is no config supplied (i.e. rendering a Forms Macro in the RTE) 2017-07-31 16:23:44 +10:00
Shannon cfd473bfe5 Fixing tests 2017-07-31 16:18:55 +10:00
Shannon b20e3c7435 ensure the user cannot access the media list dashboard if they don't have root access 2017-07-31 16:03:31 +10:00
Shannon 6de2cea9c5 U4-10023 - rte link picker disable media uploading if no start node access 2017-07-31 15:53:21 +10:00
Shannon 9db9112407 U4-10023 - rte media picker disable uploading if no start node access 2017-07-31 15:49:44 +10:00
Shannon cc8adae593 Updates ISearchableTree implementation so that a new instance is created per request 2017-07-31 15:07:09 +10:00
Stephan 19d55c8df0 U4-2676 - fix 2017-07-28 14:49:11 +02:00
Mads Rasmussen 7707bfe700 more localisation of users and user groups 2017-07-28 14:12:35 +02:00
Mads Rasmussen 47290cd801 init localise on users and groups 2017-07-28 13:25:29 +02:00
Stephan b10d370b16 Merge branch dev-v7.7 into user-group-permissions 2017-07-28 12:54:13 +02:00
Stephan 71a640de74 Merge pull request #2063 from umbraco/temp-isearchabletree
U4-2676 implement ISearchableTree to abstract search for each tree into it's own logic
2017-07-28 12:48:22 +02:00
Stephan 9c27a6c69a Merge branch 'dev-v7.7' into temp-isearchabletree 2017-07-28 12:33:13 +02:00
Stephan adac29c655 Merge pull request #2059 from umbraco/temp-U4-8643
U4-8643 Usermanagement - Store password algorithm in Usertable
2017-07-28 12:26:41 +02:00
Stephan e52adfdd07 Merge branch user-group-permissions into temp-U4-8643 2017-07-28 10:38:43 +02:00
Mads Rasmussen 43bb9ab69b set default title on user picker overlay and section picker overlay 2017-07-28 10:37:30 +02:00
Stephan 239a7632b0 Merge pull request #2057 from umbraco/temp-U4-10138
U4-10138 Cannot upgrade to 7.7 due to user groups
2017-07-28 09:55:41 +02:00
Shannon Deminick 04a7e2e04e Merge pull request #2078 from umbraco/temp-U4-10107
fixes: U4-10107 Weird UI bugs with filter dropdowns
2017-07-28 16:43:21 +10:00
Shannon 27d7bc2ae1 Adds ability to remove gravatar 2017-07-28 16:02:46 +10:00
Shannon 5ba07d56e7 Fixes model mapping for calculated start nodes and how that data gets into the user ticket. 2017-07-28 14:27:32 +10:00
Shannon 0ffd02fc61 Merge branch 'temp-U4-10175' into temp-U4-10138 2017-07-28 14:02:16 +10:00
Shannon 2eabcef69b Revert "Fix back-office identity"
This reverts commit 2d5f56cbff.

# Conflicts:
#	src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
2017-07-28 14:01:45 +10:00
Shannon 960cc32da1 Ensures the user validator doesn't execute unless the username or email addres is changed (since that is all the validator validates) 2017-07-28 12:20:10 +10:00
Mads Rasmussen ef20deb9cb fixes: U4-10202 Make it more obvious when no content or media start nodes are selected the user has access to the root node. 2017-07-27 14:55:23 +02:00
Stephan fdf206038a Merge branch user-group-permissions into temp-U4-10138 2017-07-27 13:37:49 +02:00
Stephan 6faadce477 U4-10175 - fix 2017-07-27 12:46:02 +02:00
Stephan 2d5f56cbff Fix back-office identity 2017-07-27 12:37:21 +02:00
Stephan 4569a52806 U4-10175 - fix 'edit user' link 2017-07-27 12:37:09 +02:00
Mads Rasmussen ca2896aaaa Didn't want to remove that 2017-07-27 11:03:03 +02:00
Mads Rasmussen 0b77e604f6 add button to remove avatar 2017-07-27 10:55:38 +02:00
Mads Rasmussen f50153728d manually show/hide filters 2017-07-26 15:21:30 +02:00
Stephan 52a46a2fe0 Merge branch 'user-group-permissions' into temp-U4-10175 2017-07-26 15:18:37 +02:00
Stephan da9fd1a9e8 Merge pull request #2061 from umbraco/temp-U4-10084
U4-10084 Cleanup all unused/legacy webforms editors, tinymce files and affiliated services
2017-07-26 14:49:05 +02:00
Mads Rasmussen 1692000117 umb-keyboard-list polish 2017-07-26 14:12:55 +02:00
Stephan 9972f95a10 Merge branch user-group-permissions into temp-U4-10084 2017-07-26 13:22:43 +02:00
Mads Rasmussen 964a8b400f close umb-dropdown on esc press 2017-07-26 12:16:54 +02:00
Stephan 0762bd7e76 U4-10023 - also in grid media picker 2017-07-26 11:31:19 +02:00
Stephan 15c5900a24 U4-10023 - virtual root start node in mediapicker 2017-07-26 10:44:42 +02:00
Mads Rasmussen 1ec63c3f85 add documentation to umbDropdown, umbDropdownItem and umbKeyboardList 2017-07-26 10:36:02 +02:00
Shannon fad090658f Fixes more tests 2017-07-26 18:33:09 +10:00
Shannon 61c3816bb9 Fixes a bunch of tests 2017-07-26 18:26:25 +10:00
Shannon b8e2e49a44 Updates to latest Katana libs 2017-07-26 17:50:58 +10:00
Shannon 6d9190bdb3 ensures all built in groups have -1 as start nodes for media, fixes automapper tests 2017-07-26 17:24:26 +10:00
Shannon 72998e1a6e Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-07-26 17:18:28 +10:00
Shannon ff87d8b414 Make sure the migration explicitly specifies that -1 is that start node for all built in groups 2017-07-26 17:18:05 +10:00
Stephan a8c29644d4 U4-10023 - multiple start nodes 2017-07-26 08:35:08 +02:00
Stephan bb9ec82060 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-07-25 18:39:47 +02:00
Mads Rasmussen 6e055f0d03 allow outside click to run when you click links and buttons 2017-07-25 15:59:28 +02:00
Mads Rasmussen 9b1f2352bd update view to use the new dropdown component and the list navigation directive 2017-07-25 15:58:14 +02:00
Mads Rasmussen abbee60c9c add directive to navigate a list with arrow up and down 2017-07-25 15:56:51 +02:00
Mads Rasmussen 97ec0ecd08 add umb-dropdown component 2017-07-25 15:48:04 +02:00
Shannon d232aa1f00 U4-10175 User avatar should refresh in top left corner when it's changed 2017-07-25 20:51:58 +10:00
Shannon Deminick 56b1e0902e Merge pull request #2068 from umbraco/temp-U4-10109
fixes: U4-10109 Need to indicate that granular permission entries can…
2017-07-25 18:54:25 +10:00
Shannon 17d17cfb82 Puts back in some old APIs that were removed to maintain some backwards compat with < 7.7 for users 2017-07-25 18:39:23 +10:00
Shannon fa89bef11a Merge branch 'dev-v7.7' into user-group-permissions 2017-07-25 15:16:37 +10:00
Shannon 39e4bf4579 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7
# Conflicts:
#	src/Umbraco.Web/PublishedContentExtensions.cs
2017-07-25 15:15:56 +10:00
Mads Rasmussen 5ade95d797 fixes: U4-10155 Prompt the user before deleting user groups in the user group list 2017-07-24 15:22:22 +02:00
Mads Rasmussen f043a2e4ee fixes: U4-10109 Need to indicate that granular permission entries can be edited 2017-07-24 14:18:36 +02:00
Stephan fedd9e2897 Fix User deep-cloning 2017-07-20 16:52:13 +02:00
Shannon c456c0a396 Changes the result of SearchableTreeResolver to be readonly 2017-07-20 23:02:58 +10:00
Shannon 525254efd4 Merge branch 'dev-v7.7' into temp-isearchabletree
# Conflicts:
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
#	src/Umbraco.Web/Trees/ContentTreeController.cs
2017-07-20 22:21:59 +10:00
Shannon a92288cd61 Fixes implementation of the role store on the BackOfficeUserStore 2017-07-20 22:12:13 +10:00
Shannon 97471f667d Updates the BackOfficeIdentityUser to property support ASP.NET Identity Roles 2017-07-20 22:02:32 +10:00
Shannon 7aca656919 Merge branch 'user-group-permissions' into temp-U4-10138
# Conflicts:
#	src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
#	src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
2017-07-20 20:00:05 +10:00
Shannon c309e5edaf Removes even more 2017-07-20 19:23:46 +10:00
Shannon e76ad837b9 Fixes script and partial views trees 2017-07-20 19:16:28 +10:00
Shannon 0b6ed42201 Removes a bunch more files 2017-07-20 19:15:45 +10:00
Shannon a875ede3da Removes a ton of old unused legacy files 2017-07-20 18:55:38 +10:00
Shannon 69fc7c4b7c reverts removing cook computing dll and ensures we still ship with it, removes publish.aspx 2017-07-20 16:44:28 +10:00
Shannon e9e2a86e2e U4-5546 Remove user 'channels', weblog api, and all code / db tables relevant to those 2017-07-20 15:47:26 +10:00
Shannon 540c338967 ensure the password config values are saved when the user is created/updated 2017-07-20 14:09:51 +10:00
Shannon b58799eb3d Gets everything implemented and it all works - still uses the old hashing mechanism but everything is ready to be plugged in to support any new types of hashing. 2017-07-20 13:58:42 +10:00
Shannon 85e5b023b7 Gets UserAwareMembershipProviderPasswordHasher there, updates defaults for MembershipProvider, updates default web.config so the new hashing can be used 2017-07-20 13:44:37 +10:00
Shannon 63befa55d5 Updates password changing for users to use ASP.NET Identity, there's still some more work here to do to implement the IUserAwarePasswordHashing and to result some of the TODOs. Also moved all password changing logic to PasswordChanger even for legacy membership providers. 2017-07-20 12:53:09 +10:00
Stephan 964f3bae60 Optimize start nodes calc 2017-07-19 19:26:25 +02:00
Shannon 5c75285543 U4-8643 Usermanagement - Store password algorithm in Usertable 2017-07-19 20:05:47 +10:00
Shannon ee3f977565 Fixes issue that when we re-update the user ticket we also need to refresh the CSRF tokens 2017-07-19 19:42:12 +10:00
Shannon 8f984c8dc8 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions
# Conflicts:
#	src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
2017-07-19 19:26:26 +10:00
Shannon 8df00d5525 Fixes U4-10111 Changing email on a user doesn't show the username field 2017-07-19 19:22:43 +10:00
Shannon Deminick 05d9eca904 Merge pull request #2056 from umbraco/temp-u4-10173
u4-10173
2017-07-19 19:11:12 +10:00
Stephan 2851216b88 U4-10173 - fix 2017-07-19 10:49:10 +02:00
Stephan 92dc70fc0c Merge branch temp-u4-10173 2017-07-19 10:47:05 +02:00
Shannon 38cd3dcf94 Removes the AllStartNodeIds properties from IUser, updates all unit tests to work with the new methods, ensures the calculation is cached to the user object 2017-07-19 16:15:16 +10:00
Shannon ba46430ad1 Adds notes for ActiveDirectoryBackOfficeUserPasswordChecker and throws an exception since it doesn't support auto-linking yet 2017-07-19 14:24:57 +10:00
Shannon db957c8eaa removes unused method 2017-07-19 14:15:30 +10:00
Shannon b32325bbaa Adds all change tracking to the BackOfficeIdentityUser ensures values are not persisted that are not changed, adds required null checks, removes internal method GetOrCreateBackOfficeUser 2017-07-19 14:13:42 +10:00
Stephan f28136de91 U4-10173 - fix 2017-07-18 15:54:22 +02:00
Stephan 8c3f6dfc25 U4-10173 - refactor start nodes 2017-07-18 15:09:34 +02:00
Shannon 73b107ee2a Fixing U4-10138 Cannot upgrade to 7.7 due to user groups and U4-7907 With non OAuth external login providers we should have an 'auto-link' / 'auto-create' callback option 2017-07-18 19:53:34 +10:00
Stephan 945050858e Cleanup 2017-07-18 09:11:12 +02:00
Shannon 32dc9bd275 wires up the skip button 2017-07-18 14:48:19 +10:00
Shannon f87245f73d Fixes UI for U4-9914 UserController method to upload an avatar and set password when receiving invite 2017-07-18 14:45:09 +10:00
Shannon 0008afb455 Merge branch 'dev-v7.7' into user-group-permissions
# Conflicts:
#	src/Umbraco.Core/Umbraco.Core.csproj
#	src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
2017-07-18 13:31:04 +10:00
Shannon 6f74fcf24a Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7
# Conflicts:
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web/Umbraco.Web.csproj
2017-07-18 13:27:03 +10:00
Shannon a0d3c18acc Fixes build script to work with VS 2017 (only) and fixes BuildBelle since it didn't work and is missing tools 2017-07-18 13:10:17 +10:00
Shannon Deminick bd848b00ff Merge pull request #2046 from umbraco/temp-U4-10075
U4-10075 Inherited permissions for content nodes
2017-07-18 11:56:08 +10:00
Stephan 3ac64f18d2 Bugfix UserModelMapper 2017-07-17 15:08:45 +02:00
Shannon 193682cd4c Ability to select the root node for the users start node 2017-07-17 20:09:00 +10:00
Shannon d99f7b6ff6 Ability to select the root node for the users start node 2017-07-17 19:51:51 +10:00
Shannon d1d74203ea Ability to select root node in user group picker so the user group can explicitly allow root access or not. 2017-07-17 19:40:45 +10:00
Shannon e135291980 Fixes issue with user group start nodes, they are allowed to have null and by default this will be the case but we couldn't remove them and the ROOT node wasn't being mapped. 2017-07-17 18:23:20 +10:00
Stephan 04daaef7b7 Merge branch 'user-group-permissions' into temp-U4-10075 2017-07-14 20:15:51 +02:00
Stephan 29aaa26022 U4-10023 - fix media picker for multiple start nodes 2017-07-14 20:13:57 +02:00
Shannon Deminick 3f6ec6e36f Merge pull request #2045 from umbraco/temp-U4-10106
U4-10106 Cannot delete user groups
2017-07-14 10:53:27 +10:00
Shannon 8347d8f23b Merge remote-tracking branch 'origin/user-group-permissions' into temp-U4-10106
# Conflicts:
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
2017-07-14 10:53:07 +10:00
Shannon db863a4592 update sln to stop building the damn UI project 2017-07-14 10:49:10 +10:00
Shannon ca104347b3 Merge branch 'user-group-permissions' into temp-U4-10106 2017-07-14 10:43:45 +10:00
Shannon 9fbf1aedac adds both post and delete verbs 2017-07-14 10:43:12 +10:00
Shannon Deminick dfbd064de6 Merge pull request #2044 from umbraco/temp-U4-10096
U4-10096 Ability to bulk assign users to groups in the user list
2017-07-14 10:35:49 +10:00
Stephan 2bff1bfbb6 U4-10121 - properly dispose context 2017-07-13 14:20:29 +02:00
Stephan ab1dade468 U4-10121 - properly dispose context 2017-07-13 13:11:46 +02:00
Shannon 1643eec62c Fixes cache refreshing for user groups since user cache need to be cleared for that too. Updates cache refreshers to use json.net for serializing which is way faster. adds better support for querying multiple user groups and entity ids for permissions, updates tests 2017-07-13 18:47:10 +10:00
Claus 6a48c1646d checking for uses of userType. 2017-07-13 09:26:48 +02:00
Shannon 6e89202002 Fixes usages of IUserService.GetPermission since before it was doing a FirstOrDefault() since that's just how the old APIs worked, now we need to get all permissions based on groups. Also updated the usages of ToString for char for the permissions to be culture invariant everywhere. 2017-07-13 14:32:43 +10:00
Shannon 69a6a80ffd Updates logic and test for getting all node permissions for a user that have explicit permissions defined 2017-07-13 14:16:13 +10:00
Shannon c472463f37 Removes all user permissions cache refreshing and events and obsoletes assocated clsses 2017-07-13 11:02:34 +10:00
Shannon 76f9e2c22a Updates user permissions lookup to do one query instead of one query per user group 2017-07-13 10:43:42 +10:00
Shannon df18cdc7f7 Merge remote-tracking branch 'origin/user-group-permissions' into temp-U4-10075 2017-07-13 01:58:18 +10:00
Shannon 87dcdc596e Gets last unit tests written for returning all permissions for either user or group when not specifying Ids 2017-07-13 01:56:22 +10:00
Shannon 044120ffd2 Gets inherited permissions working, adds lots of unit tests, few more tests to write now. 2017-07-13 00:07:51 +10:00
Claus 0acb0d48a1 Merge pull request #1858 from Jeavon/RelatedLinks-Tweaks
Tweaks to RelatedLinks
2017-07-12 15:26:53 +02:00
Claus c8828b968c found an extension method :) 2017-07-12 13:56:18 +02:00
Claus 2e28ff3ccf U4-10106 Cannot delete user groups 2017-07-12 13:43:00 +02:00
Stephan f18b7110c9 Minor fix in DataTypeValidateAttribute 2017-07-12 12:26:51 +02:00
Claus 44bb7e84db U4-10142 Adding a user - does not show up until refresh 2017-07-12 11:52:08 +02:00
Claus 06f68b1417 U4-10096 Ability to bulk assign users to groups in the user list 2017-07-12 11:31:34 +02:00
Shannon 7115d85440 Removes unused and unneeded APIs on IUserService, updates code comments to describe implicit vs explicit permissions 2017-07-12 14:06:30 +10:00
Shannon d9937fb276 Stop trying to build the UI.Client project as part of the sln build 2017-07-12 14:01:36 +10:00
Claus 7f98f99eaa Merge pull request #2039 from umbraco/temp-u4-10121
U4-10121 - Scheduled Publishing as background task
2017-07-11 11:37:46 +02:00
Shannon 53bce239c3 Gets implicit/inherited permissions 'working', well at least with one test, now need to test the rest. 2017-07-11 19:19:38 +10:00
Shannon 50a896a6cc Doesn't send async if not using a network based SMTP server 2017-07-11 15:06:18 +10:00
Shannon 3c31e33000 Fixes U4-10133 Investigate issues with SMTP Invite and async 2017-07-11 14:46:51 +10:00
Shannon 9462f8ad85 Fixes password reset when there is invalid other password properties 2017-07-11 13:57:33 +10:00
Shannon cb4e1d324d Removes the NoCredentials enum (we don't use it or need it) 2017-07-11 12:18:12 +10:00
Stephan 13d26eb435 Merge branch dev-v7.7 into user-group-permissions 2017-07-10 17:48:51 +02:00
Claus 6a06a9e400 small tweak to skip a check if not needed. 2017-07-10 15:27:15 +02:00
Claus 1149129c6f U4-10104 Dashboards are not showing up in backoffice 2017-07-10 15:21:24 +02:00
Claus ad886efa62 cleanup and lineendings. 2017-07-10 15:20:17 +02:00
Stephan 79df75c415 U4-10121 - Scheduled Publishing as background task 2017-07-10 13:39:40 +02:00
Claus c0a6c58a07 U4-10134 Disabled doesn't override Invited 2017-07-10 13:38:39 +02:00
Claus deaece7cfc more clauses. 2017-07-10 13:37:57 +02:00
Claus a3bb11e4b0 I got a feeling you did this on purpose :P 2017-07-10 13:05:33 +02:00
Claus f2b52585e1 U4-10111 Changing email on a user doesn't show the username field 2017-07-10 12:22:35 +02:00
Claus 8b308fa1ea hide the count until the property is set. 2017-07-10 12:22:12 +02:00
Sebastiaan Janssen e9072aa6c9 Merge pull request #2031 from umbraco/temp-U4-10117
Adds a missing index to the cmsDictionary.key column with migrations since the column size needs to be reduced.
2017-07-06 15:31:05 +02:00
Claus 779306d90f typos. 2017-07-06 15:08:01 +02:00
Claus 7540f49a5e fixing a bunch of mismatching mappings.
aligning property names with what they actually are.
2017-07-06 14:41:05 +02:00
Claus 2e29d112e5 reformatting. 2017-07-06 13:14:24 +02:00
Claus fe451154d1 fixing some markup errors 2017-07-06 13:14:17 +02:00
Claus 271056f997 mapping media start nodes instead of mapping content twice. 2017-07-06 12:48:47 +02:00
Shannon 9b0f2145d0 Fixes user state filtering to do an OR 2017-07-06 19:51:59 +10:00
Shannon f355b27d1f Merge branch 'dev-v7.7' into user-group-permissions 2017-07-06 12:56:17 +10:00
Shannon 8e861eab43 Adds a missing index to the cmsDictionary.key column with migrations since the column size needs to be reduced. 2017-07-06 12:45:58 +10:00
Shannon 7e9afe7720 bumps version 2017-07-06 12:31:26 +10:00
Shannon a050a73c31 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7 2017-07-06 12:28:51 +10:00
Shannon 403a210c1c Fixes issue with assigning default permissions for a new user group 2017-07-05 19:25:48 +10:00
Shannon 03c7429653 FIxes SQL statement when upgrading with SQLCE and fixes GetAll when passing in an array of ints to ensure that we check if the int array is empty or not 2017-07-05 18:23:51 +10:00
Shannon bba7b15125 Fixes installer angular module loading - this would have never worked without errors, not sure how we've missed this. 2017-07-05 18:22:45 +10:00
Shannon 1d9e33dadf Granular permissions in group editor are all working, we could do with an edit button but for now it's ok, selecting the same node will perform an 'edit' 2017-07-04 15:40:33 +10:00
Shannon 316ec5d20f gets granular permissions showing correctly in group editor 2017-07-04 15:07:11 +10:00
Shannon 82bd11e66d Gets filtering on user state working 2017-07-03 19:27:48 +10:00
Shannon 94b0cb04da fix tests 2017-07-03 17:58:04 +10:00
Shannon ff02b28d26 Fixes tree for single tree sections, fixes creating a user group so you can select default permissions 2017-07-03 16:31:47 +10:00
Shannon 247ca906aa Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-07-03 15:10:10 +10:00
Mads Rasmussen bcffc37287 revert this for now 2017-06-30 21:29:24 +02:00
Mads Rasmussen 8d2a9d1d81 fixes: U4-10060 Make UI for user groups overview 2017-06-30 13:33:59 +02:00
Mads Rasmussen 2aaf104856 change text on cancel button to close 2017-06-30 10:51:23 +02:00
Shannon 224d1bcf6e U4-10053 Review anywhere else in the codebase that may reference User Type
and removes all old editors, trees, etc...
2017-06-30 18:38:02 +10:00
Mads Rasmussen 6ebacf118e fix grunt build 2017-06-30 10:36:55 +02:00
Shannon e4edd8f692 U4-10053 Review anywhere else in the codebase that may reference User Type
This also removes some totally unused and ancient code
2017-06-30 18:14:44 +10:00
Shannon 8fe904cea7 changing spaces back to 4, finally in sync with mads 2017-06-30 17:24:48 +10:00
Shannon 3a17002ec4 fixes build after merge 2017-06-30 17:21:40 +10:00
Shannon 804c60653e Merge branch 'dev-v7.7' into user-group-permissions
# Conflicts:
#	src/Umbraco.Core/Services/UserService.cs
#	src/Umbraco.Tests/Services/ContentServiceTests.cs
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI/umbraco/config/lang/nl.xml
2017-06-30 16:57:22 +10:00
Shannon 29c6d547db Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7 2017-06-30 16:55:04 +10:00
Shannon 546999e470 U4-9946 Update the ExternalSignInAutoLinkOptions to support groups - currently still using UserType 2017-06-30 16:50:44 +10:00
Shannon 0da75f4e78 Updates content permissions dialog to show the explicit permissions assigned and removing them will reset them. 2017-06-30 16:32:01 +10:00
Shannon e69957e580 Merge remote-tracking branch 'origin/user-group-permissions' into user-group-permissions 2017-06-30 14:45:07 +10:00
Shannon a072d4edb3 adds security to changing premissions in the content dialog 2017-06-30 14:42:08 +10:00
Mads Rasmussen 968f93f769 stop toggles from closing the dialogs 2017-06-29 15:28:38 +02:00
Mads Rasmussen 0c6c3f6d51 add notifications to content permissions dialog 2017-06-29 15:12:12 +02:00
Mads Rasmussen a082b72f50 set change password button size to the same as the other buttons 2017-06-29 12:30:50 +02:00
Mads Rasmussen 69791a3616 use userGroups resource in userGroups picker 2017-06-29 12:25:28 +02:00
Shannon 72bdc0f070 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-29 19:15:22 +10:00
Shannon 4f28cb1917 Gets content permissions saving per user group 2017-06-29 19:15:04 +10:00
Claus 6cb104cd4f Merge pull request #2023 from umbraco/drbldgtl-dev-v7-U4-5804
U4-5804 Remove all the old images that are no longer used in the umbraco and umbraco_client folders
2017-06-29 10:50:09 +02:00
Claus af3665c0f9 Merge branch 'dev-v7-U4-5804' of git://github.com/drbldgtl/Umbraco-CMS into drbldgtl-dev-v7-U4-5804
# Conflicts:
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
2017-06-29 10:48:04 +02:00
Mads Rasmussen ed78082693 aligning UI for pickers 2017-06-29 10:15:10 +02:00
Mads Rasmussen 91e15d4b64 use userGroupsResource in users and groups overview 2017-06-29 09:57:24 +02:00
Shannon 5dec4a4a94 Moves logic to separate UserGroupsController from the user controller, refactors a few model mappings to reduce a few lines of code, default permissions on user groups now save 2017-06-29 14:39:11 +10:00
Shannon 5d5b852e5a moves the change password part of the user to where the design had it 2017-06-29 13:17:19 +10:00
Shannon 454684e108 updates user models to not return so much info when not needed 2017-06-29 13:13:32 +10:00
Shannon 4fc5279a78 ok, tests fixed now? 2017-06-29 12:38:33 +10:00
Shannon abb393c335 fixes tests 2017-06-29 12:31:23 +10:00
Mads Rasmussen 225cf8f8f5 fix content permissions dialog 2017-06-28 22:11:02 +02:00
Mads Rasmussen 0bc2e93230 fix grunt build 2017-06-28 21:52:33 +02:00
Shannon 7ca7b6f922 Allows for the mini profiler in the back office (again) 2017-06-29 01:04:20 +10:00
Shannon 923ceadd02 fixes new permissions model mapping on dialog 2017-06-29 01:03:27 +10:00
Shannon c5111f9ba8 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/content/rights.html
2017-06-29 01:01:54 +10:00
Shannon 85d988e75e Adds GetDetailedPermissions to the content controller which is used in the permissions dialog 2017-06-29 01:00:52 +10:00
Mads Rasmussen 49f2892ff1 group.controller.js: a bit of clean up 2017-06-28 16:12:58 +02:00
Mads Rasmussen c7c0793a0a fix line indention 2017-06-28 16:06:54 +02:00
Mads Rasmussen 3d847b0ea9 fixes: U4-10054 Prototype granular permissions on user group 2017-06-28 16:03:33 +02:00
Shannon 3cd39fee94 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-28 23:38:51 +10:00
Shannon 8023a89b10 Returns the default permissions as a model as part of the UserGroupDisplay and wires this up in the user group editor 2017-06-28 23:38:42 +10:00
Claus 4f6ecf5e91 Merge branch 'temp-old-client-files-clean-up-2' into dev-v7.7 2017-06-28 15:27:34 +02:00
Claus eb4588780e Hartvig removing a lot of old client files used only in v6.
Reverted on dev-v7 and cherrypicked onto dev-v7.7.
2017-06-28 15:01:56 +02:00
Mads Rasmussen de00b2ef00 fixes: U4-10061 Add functionality for show/hide/copy password when creating new user 2017-06-28 10:29:16 +02:00
Mads Rasmussen 2294940038 bring back the avatar in the edit user editor 2017-06-27 12:53:41 +02:00
Shannon e8e2e2c91d Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-27 19:55:21 +10:00
Shannon d78dcb51c0 Gets the invite fully working along with choosing an avatar, adds more security so that the link can't be used again, 2017-06-27 19:55:03 +10:00
Mads Rasmussen 8eb4d2ed20 fixes: U4-10062 Restrict current user permissions 2017-06-27 11:40:04 +02:00
Mads Rasmussen bc19c8358d update to edit user to support state key instead of state alias 2017-06-27 11:31:41 +02:00
Jeavon ce38320c6a Tidy up usings 2017-06-27 09:58:12 +01:00
Mads Rasmussen 439df3b52c user group preview - only show edit button if edit is allowed 2017-06-27 10:07:16 +02:00
Shannon ab71fa3101 adds the IsCurrentUser flag to the UserDisplay model 2017-06-27 15:44:55 +10:00
Shannon 8e21bc3c74 Create user now generates a password 2017-06-27 15:30:32 +10:00
Jeavon 3083f185d0 Merge branch 'dev-v7.7' into temp-U4-9984 2017-06-26 18:01:57 +01:00
Jeavon 07356db30e Adding MachineName to Slack Notification username so you tell where it came from in different environments 2017-06-26 16:34:38 +01:00
Jeavon f615125c24 Remove Slack options from HealthCheckResults as it now looks after itself 2017-06-26 16:28:44 +01:00
Jeavon 29da5ed843 Fix HttpsCheck to use HealthCheckContext instead of HttpContext so it works with scheduled checks 2017-06-26 16:26:18 +01:00
Jeavon 587ce8fc4f My "and" as in the wrong place 2017-06-26 16:05:15 +01:00
Jeavon 833c787414 Adding message text with totals 2017-06-26 15:53:15 +01:00
Jeavon 1f2e1e4e93 Warnings are success with Warning so should be counted as success 2017-06-26 15:48:44 +01:00
Jeavon d7bd711d9a Switch Health Check Slack notification to use Slack Attachments 2017-06-26 15:25:11 +01:00
Mads Rasmussen f309e3587b fix user group filter + add UI for user states filter 2017-06-26 15:00:32 +02:00
Mads Rasmussen 493873a8d0 permissions dialog: add edit button to selected user group 2017-06-25 22:48:49 +02:00
Mads Rasmussen 7beec02a3f permissions dialog: fix save button text 2017-06-25 21:54:32 +02:00
Mads Rasmussen 86cb750ca1 permissions dialog: filter out selected groups from dropdown 2017-06-25 21:51:17 +02:00
Mads Rasmussen f92e119060 hardcode group names 2017-06-25 20:27:14 +02:00
Mads Rasmussen 13a0621f89 localize strings 2017-06-25 20:26:44 +02:00
Shannon 0cd871241f Gets password reset working, gets more server side validation wired up 2017-06-23 16:38:39 +10:00
Shannon 3eba5e5c19 Merge branch 'dev-v7.7' into user-group-permissions
# Conflicts:
#	src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
#	src/Umbraco.Tests/Services/UserServiceTests.cs
#	src/Umbraco.Web.UI.Client/src/common/services/util.service.js
#	src/Umbraco.Web.UI/config/trees.config
#	src/Umbraco.Web/Umbraco.Web.csproj
2017-06-23 16:37:56 +10:00
Shannon 754dbc9621 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7
# Conflicts:
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
2017-06-23 15:15:55 +10:00
Shannon d936105cfe Gets the user states set correctly 2017-06-23 15:14:46 +10:00
Shannon ea2f808c1e fixes tests 2017-06-23 14:58:00 +10:00
Shannon 3ece9175a0 Moves the enable/disable user notifications and localization alerts to server side, adds server side validation so that the current user cannot disable themsevlves. 2017-06-23 14:49:04 +10:00
Shannon 692dc66684 You can now change the user password in the user editor 2017-06-23 14:08:58 +10:00
Shannon 25a79bc2fa fixing tests 2017-06-23 11:43:58 +10:00
Shannon 8cd413ba8f ensures group start nodes are set to null on install 2017-06-23 11:24:06 +10:00
Shannon 1eeae770b4 Fixes issue with calculating all user start nodes, improves perf too 2017-06-23 11:23:40 +10:00
Mads Rasmussen 0e2c94c079 fix remove user from user group 2017-06-22 14:59:03 +02:00
Mads Rasmussen 99da2e923d move toggle to the left of the permission text 2017-06-22 13:30:36 +02:00
Mads Rasmussen c9200d54b5 update umb-toggle to show icons as default instead of labels 2017-06-22 13:28:01 +02:00
Shannon 8f51f574ae fixing build 2017-06-22 17:02:46 +10:00
Shannon 90d4153e87 updates some of the sub view routing with users/groups so the routing is always there 2017-06-22 16:47:45 +10:00
Shannon 6380d1d538 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-22 16:33:00 +10:00
Shannon b43ca64ac7 Ensures that the auth cookie data contains all of the user's start nodes, fixes persisting the user group icon 2017-06-22 16:32:44 +10:00
Mads Rasmussen 7825040e4d sync tree 2017-06-21 15:55:57 +02:00
Mads Rasmussen f27b456eb4 users overview tlc + cleanup. Changed click to open instead of select 2017-06-21 15:40:52 +02:00
Mads Rasmussen bd7aba5ea2 fix umb-toggle labels + add documentation 2017-06-21 11:35:53 +02:00
Mads Rasmussen 9786ae8428 wip content permissions dialog 2017-06-20 15:35:02 +02:00
Mads Rasmussen f28c4cdaa5 load new permissions dialog 2017-06-20 15:19:42 +02:00
Mads Rasmussen cc41447ff7 prototype default permissions on edit user group 2017-06-20 15:19:06 +02:00
Mads Rasmussen 2e239939fc add umb-toggle component 2017-06-20 14:52:32 +02:00
Stephan 4cdb30fed0 Merge branch dev-v7 into dev-v7.7 2017-06-20 14:27:25 +02:00
Shannon 9dc34f0c98 Updating a bunch of start node checking logic to ensure that it takes the user and user group start nodes into account 2017-06-20 18:38:31 +10:00
Shannon cec829774e Merge remote-tracking branch 'origin/dev-v7' into user-group-permissions
# Conflicts:
#	src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
#	src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs
2017-06-20 17:09:52 +10:00
Shannon 94e958bd7a Ensures the user's combined start nodes are used when checking path access 2017-06-20 17:04:00 +10:00
Shannon 957c8e917f fixes tests 2017-06-20 16:49:24 +10:00
Shannon d7e65dd088 User groups can now be created/saved 2017-06-20 15:10:42 +10:00
Shannon 4e1e2c5332 fix unit tests 2017-06-20 14:37:49 +10:00
Shannon 7689ee73d4 Wires up saving the user group 2017-06-20 14:33:24 +10:00
Shannon af7a5187f2 Adds the user count to IUserGroup 2017-06-20 13:19:14 +10:00
Shannon e276c7a1bf Wires up getUserGroupScaffold 2017-06-20 12:34:33 +10:00
Shannon 7797ad4130 localizes invite email 2017-06-20 12:25:27 +10:00
Shannon 78bfc087fb Fixes up all of the user sorting to work properly 2017-06-20 11:55:21 +10:00
Shannon 146a865882 fixes UserState values to be what they were before 2017-06-20 11:21:31 +10:00
Jeavon b0827b75b2 Rename Results to GetResultsForStatus and only calculate the result set required 2017-06-19 13:03:29 +01:00
Jeavon 824eee80b3 Adding a Results method to the HealthCheckResults so that Notification Methods can construct specific messages with different result sets 2017-06-19 12:18:47 +01:00
Jeavon 0638786075 A Resultype of info only isn't considered a failure 2017-06-19 12:16:01 +01:00
Mads Rasmussen 79e9394b62 wire up enable users and disable users bulk actions 2017-06-19 11:51:38 +02:00
Jeavon ebef6b5d42 A few tweaks to the HealthChecks.Release.config to show different options 2017-06-19 10:11:57 +01:00
Shannon 054fd46555 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-19 18:45:53 +10:00
Shannon 9ab9007821 SQL fix for SQLCE for user states 2017-06-19 18:45:38 +10:00
Mads Rasmussen a7a7909a25 fix header icon width 2017-06-19 10:21:21 +02:00
Jeavon ac1aa719b6 Updating HealthChecks.Release.config to new format 2017-06-19 09:18:00 +01:00
AndyButland 9f68bd4e52 Amended registration of health check scheduled notifiers (email and Slack) to use a resolver method and allow others to be added without modifying core 2017-06-18 16:41:46 +02:00
Mads Rasmussen 41ee187637 increase font small for small buttons 2017-06-16 14:14:05 +02:00
Mads Rasmussen 3ec9c9a348 fix animation on block buttons 2017-06-16 14:13:44 +02:00
Mads Rasmussen efaa24fb51 add UI feedback when enabling/disabling user 2017-06-16 14:03:06 +02:00
Mads Rasmussen 00659711d9 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-15 20:28:39 +02:00
Shannon 021e083b75 Fixes issue with email confirmation date, adds invite date, adds user state counts 2017-06-15 16:47:51 +02:00
Shannon 0f89b1dea7 Fixes up how formctrl are required for various directives, adds validation to invite/create user, fixes up back end and is all now working 2017-06-15 15:59:29 +02:00
Shannon 5b8d314a29 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-15 12:47:40 +02:00
Shannon f33d21b8f6 Gets all the localization for the invite setup and error checking based on the password provider information 2017-06-15 12:45:53 +02:00
Mads Rasmussen 2361d7878e add word-wrap to user-card name 2017-06-15 11:27:01 +02:00
Mads Rasmussen 613a9de858 show user state after save 2017-06-15 11:20:19 +02:00
Mads Rasmussen 9f234f1669 start on create user group 2017-06-15 11:07:57 +02:00
Mads Rasmussen 7353591f4c in sections panel: show avatar initials if no avatar 2017-06-15 11:06:10 +02:00
Shannon 439c30e2fd changes name from versionHash to cacheBuster 2017-06-15 09:20:46 +02:00
Shannon 0858e04345 tidy up how a user is created when invited, ensure the special empty password is used. 2017-06-15 00:46:23 +02:00
Shannon 6d22f7f86f Getting the invite password setting done with validation, almost there 2017-06-15 00:05:12 +02:00
Shannon c13b2fae4b Fixes issue of rendering the umb version in the html markup 2017-06-15 00:04:42 +02:00
Shannon 51b19bedc8 Merge remote-tracking branch 'origin/dev-v7' into user-group-permissions
# Conflicts:
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web/Editors/GravatarController.cs
#	src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
2017-06-14 22:30:36 +02:00
Shannon 582e36c517 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-14 21:30:08 +02:00
Shannon a62426a763 Gets email sending ensures user is logged in with verifying but is only partially logged in and cannot access the back office, they can only access the verification screen 2017-06-14 16:21:56 +02:00
Mads Rasmussen 898f00cbf1 fix avatar sizes 2017-06-14 14:07:07 +02:00
Mads Rasmussen 12de575dfb update user picker to match new data model 2017-06-14 13:12:49 +02:00
Mads Rasmussen cd8418a8c0 remove fake loading of user groups 2017-06-14 12:52:12 +02:00
Mads Rasmussen 89dcb6a0e2 fix start node pickers 2017-06-14 12:51:41 +02:00
Shannon cd1e88d151 Gets email sending using razor view 2017-06-14 11:29:15 +02:00
Shannon e757868d09 Moves logic back to user service for inviting a user since its much simpler. 2017-06-13 18:47:20 +02:00
Shannon f09f17e496 getting email invite working and with identity apis 2017-06-13 18:38:16 +02:00
Shannon 3fac1b05ae Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-13 15:48:04 +02:00
Shannon 716629235e issue with mapping ysod 2017-06-13 15:47:39 +02:00
Mads Rasmussen e40a904896 clean up groups overview controller 2017-06-13 15:39:14 +02:00
Mads Rasmussen 5aeb8ed74d get all sections in section picker 2017-06-13 15:27:58 +02:00
Mads Rasmussen 5273644a7e split up groups and status filter + add additional filter options to order by 2017-06-13 15:12:54 +02:00
Mads Rasmussen 56e6722880 fix user group load spinner 2017-06-13 11:47:35 +02:00
Mads Rasmussen fb3ac86d8c add section picker 2017-06-13 11:30:48 +02:00
Jeavon Leopold 01a2ba8ad6 Added failureOnly setting to both email and Slack health check notifications - thanks to feedback at #cg17! 2017-06-10 09:59:37 +02:00
Jeavon Leopold 36a659f3da Incorrect class for logging within the HealthCheckNotifier 2017-06-10 09:34:35 +02:00
Jeavon Leopold f6cc06356c Adding new verbosity attribute to transform for health check config 2017-06-06 12:24:16 +02:00
AndyButland 7929b460c3 Added option for summary or detailed results in health check notifications 2017-06-06 10:32:26 +02:00
Mads Rasmussen 379b8c3719 fix render in users list view 2017-06-06 02:16:21 +02:00
Shannon 174e336512 fixes sections 2017-06-06 02:13:14 +02:00
Shannon 9bf61bb487 real data for user group 2017-06-06 02:05:17 +02:00
Shannon 5a5bdb6328 user doesn't need to be not approved for invite 2017-06-06 01:29:01 +02:00
Shannon 7f03a97a78 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-06 01:21:17 +02:00
Shannon 7533b0e704 ensures icons, ensures saving info for creating users 2017-06-06 01:21:03 +02:00
Mads Rasmussen d72af87790 rename content picker to media picker 2017-06-06 01:11:40 +02:00
Mads Rasmussen 1eba16579a remove search on user groups 2017-06-06 01:10:55 +02:00
Mads Rasmussen 1c373ca771 fix render in user group picker 2017-06-06 01:10:36 +02:00
Shannon f907385ac8 fix mapping issue 2017-06-06 01:06:19 +02:00
Shannon fbda8fe4a3 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-06 00:50:56 +02:00
Mads Rasmussen e8ceb2756d wire up start node preview 2017-06-06 00:49:48 +02:00
Shannon cfe90999d3 Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-06 00:49:43 +02:00
Shannon bbda93a06b updates mapping for start nodes 2017-06-06 00:49:36 +02:00
Mads Rasmussen a041fc47e0 view for user group preview 2017-06-06 00:49:07 +02:00
Mads Rasmussen 6da5fe6b3a fix sections, content and media start node in umbUserGroupPreview 2017-06-06 00:35:31 +02:00
Shannon 869f75af4a Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-06-06 00:14:26 +02:00
Shannon 76032cf5f7 maps name and icon for user groups 2017-06-06 00:13:46 +02:00
Mads Rasmussen c03e1420e0 fix user group name 2017-06-06 00:11:59 +02:00
Shannon 64975166e7 updates user models 2017-06-06 00:02:23 +02:00
Shannon 2ee34d5c48 updates user models 2017-06-05 23:25:33 +02:00
Lee Kelleher 44018b976a Merge pull request #1980 from leekelleher/temp-cgrt17-nc-tweaks-1
Nested Content amends + localization
2017-06-05 21:40:51 +02:00
leekelleher 1293730dd3 Merge remote-tracking branch 'upstream/dev-v7.7' into temp-cgrt17-nc-tweaks-1
# Conflicts:
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
2017-06-05 21:35:16 +02:00
Shannon Deminick fc30cdee31 Merge pull request #1984 from umbraco/temp-content-blueprints
Content blueprints
2017-06-05 21:26:35 +02:00
Shannon ca8e25f465 Merge remote-tracking branch 'origin/dev-v7.7' into temp-content-blueprints 2017-06-05 21:17:53 +02:00
Shannon f55d1328d0 Merge tag 'release-7.6.4' into temp-content-blueprints 2017-06-05 21:17:41 +02:00
leekelleher 685f7bcde8 Correct the Nested Content Editor directive name,
as it conflicted with the package version - which override the core version (wasn't good).
2017-06-05 21:08:43 +02:00
Lars-Erik Aabech 6c78b8b60a Prevents saving blueprint with same name as other existing 2017-06-05 16:56:29 +02:00
Lars-Erik Aabech 8602aab6f7 Merge remote-tracking branch 'origin/temp-content-blueprints' into temp-content-blueprints 2017-06-05 16:47:40 +02:00
Lars-Erik Aabech 88702f7e18 Can create blueprints from the tree 2017-06-05 16:46:30 +02:00
Jeavon Leopold 57bb47a78b Add xdt transforms for adding the HealthChecks config to Web.Config 2017-06-05 15:31:21 +02:00
leekelleher 57b037fda0 Switched to a better GUID generation method
Fixed issue with keys being generated on every publish rather than being remembered
2017-06-05 15:26:02 +02:00
leekelleher 7aad660ebf Renamed selectedDocTypeTabs to be docTypeTabs,
as it contains the tab names for all the doctypes. It was a misnomer.
2017-06-05 15:25:34 +02:00
leekelleher 181a5b847c Removed redundant reference from the Nested Content editor controller
`ncResources` wasn't being used in the `PropertyEditorController`
2017-06-05 15:24:37 +02:00
leekelleher 7a7f24ed78 Localized Nested Content's labels 2017-06-05 15:23:27 +02:00
leekelleher 0c8e5a2cc2 Added dictionary keys for Nested Content 2017-06-05 15:21:43 +02:00
leekelleher c43dda468c Renamed "nestedcontent.resources.js" to be singular "resource".
Aligns with the rest of the resource files
2017-06-05 15:21:21 +02:00
Jeavon Leopold 9edc20b1b1 Merge branch 'dev-v7.7' into temp-U4-9984 2017-06-05 15:00:42 +02:00
AndyButland 28247add3c Created unit tests for health check results 2017-06-05 14:37:50 +02:00
Shannon e4d96997d5 Merge remote-tracking branch 'lars/temp-content-blueprints' into temp-content-blueprints 2017-06-05 14:35:07 +02:00
Jeavon Leopold dd84164ac5 Fix email subject to come from config for Health Check Notifier 2017-06-05 14:08:52 +02:00
Shannon 8bc033efde Merge remote-tracking branch 'lars/temp-content-blueprints' into temp-content-blueprints 2017-06-05 14:02:23 +02:00
Jeavon Leopold a407f9aa19 Merge branch 'feature/health-check-scheduler' of https://github.com/AndyButland/Umbraco-CMS into temp-U4-9984
# Conflicts:
#	src/Umbraco.Web/HealthCheck/HealthCheckResults.cs
#	src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs
2017-06-05 14:02:12 +02:00
Shannon 1e90015253 updates to name ModelState and ensures tokenized localization 2017-06-05 14:02:11 +02:00
Lars-Erik Aabech 7f63919ba6 Merge branch 'temp-content-blueprints' of https://github.com/umbraco/Umbraco-CMS into temp-content-blueprints 2017-06-05 13:52:57 +02:00
Jeavon Leopold 57ce6d7f64 Break up the list of Health Checks to be more readable and change the Slack icons 2017-06-05 13:52:48 +02:00
Lars-Erik Aabech 0fff051cf6 Tests for the create content controller 2017-06-05 13:52:07 +02:00
AndyButland 0a997ae04e Switched health check resolver to use application scope 2017-06-05 13:40:38 +02:00
AndyButland bf194a819a Added try/catch around scheduled checks so one failure doesn't block the rest. Applied some formatting to email HTML notification. 2017-06-05 13:35:08 +02:00
Jeavon Leopold a102a7c094 Adding Slack.Webhooks to nuspec 2017-06-05 13:30:26 +02:00
Lars-Erik Aabech 346b29b827 Couple of unit tests for the create content dialog 2017-06-05 12:12:25 +02:00
Shannon 1bedc6a444 Wires up server side validation for name 2017-06-05 12:11:20 +02:00
Jeavon Leopold 826f916960 Health Check notifier now has different icons depending on if all checks are good or not 2017-06-05 12:10:19 +02:00
Jeavon Leopold d30b70ea12 Refactoring the HealthChecks Configuration to make it cleaner & easier to add futher notification providers 2017-06-05 11:57:03 +02:00
AndyButland 0f7165dada Retrieved HTML fragment for health check notification email from results, minor reformatting 2017-06-05 11:45:42 +02:00
Stephan 31f8213460 Amazingly improve NestedContent non-support of some editors 2017-06-05 11:33:05 +02:00
Jeavon Leopold 6b419f4260 Adding check for server role to the HealthCheckNotifier so it only runs on the Master 2017-06-05 11:26:33 +02:00
Jeavon Leopold ddd693e120 Merge branch 'feature/health-check-scheduler' of https://github.com/AndyButland/Umbraco-CMS into temp-U4-9984 2017-06-05 11:17:01 +02:00
Jeavon Leopold 7047d3ebaa Adding support for Slack Markdown "light" 2017-06-05 11:16:03 +02:00
AndyButland 84d131e7f7 Removed disabled healthchecks from dashboard 2017-06-05 11:07:12 +02:00
AndyButland f75b66dda8 Send scheduled health checks results as email to configured address 2017-06-05 10:52:17 +02:00
Lars-Erik Aabech 1abec65f03 Saving blueprints keeps only save button 2017-06-05 10:47:52 +02:00
Shannon ac0a5bfe5d fixes cancel button 2017-06-05 10:46:37 +02:00
Jeavon Leopold 6e4ffcc068 Adding method to HealthCheckResults to return the results as Markdown 2017-06-05 10:40:20 +02:00
Shannon 121036ebb9 Merge branch 'dev-v7.7' into user-group-permissions
# Conflicts:
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Web.UI.Client/src/less/belle.less
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web/Umbraco.Web.csproj
2017-06-05 10:35:23 +02:00
Shannon e2f9c90dc6 Merge branch 'dev-v7.7' into temp-content-blueprints 2017-06-05 10:23:10 +02:00
Jeavon Leopold 439d52e358 Merge branch 'feature/health-check-scheduler' of https://github.com/AndyButland/Umbraco-CMS into temp-U4-9984 2017-06-05 10:18:26 +02:00
Shannon 7d1aedc0d3 fixes unit tests 2017-06-05 10:17:18 +02:00
Jeavon Leopold 48864e3fa4 Initial addition of Slack Webhook Notifications for Health Check notifications 2017-06-05 10:15:26 +02:00
AndyButland 474dcf0779 Improved layout of scheduled healthcheck result logging 2017-06-05 10:14:47 +02:00
AndyButland 0366fa73c3 Remove requirement for configuring start time for scheduled health checks; if not provided, start with short delay after application start 2017-06-05 10:14:17 +02:00
AndyButland 9b6cad7d82 Fixed health check configuration following namespace refactor 2017-06-05 10:08:57 +02:00
Shannon 3a990d9cf0 Merge remote-tracking branch 'origin/temp-u4-8604-1' into dev-v7.7 2017-06-05 09:50:17 +02:00
Stephan 832ee47564 Fix nested content prevalue cache of all things 2017-06-05 09:28:52 +02:00
leekelleher dc3f96a292 Included a pull-request from NestedContent package
Fixes the prevalue-editor's doctype tabs dropdownlist from reverting to default post-save.

https://github.com/umco/umbraco-nested-content/pull/131
2017-06-05 09:28:52 +02:00
leekelleher 7806246a52 Added NestedContent to "lists" datatype group.
Set the icon to "icon-thumbnail-list".
2017-06-05 09:28:52 +02:00
leekelleher e136780947 Fixed JSLint warnings
Renamed "nestedcontent.filters.js" filename to be singular.
2017-06-05 09:28:52 +02:00
Stephan b9ac5a365a Sanitize editors names 2017-06-05 09:28:52 +02:00
Stephan 942d010264 Retreat Amazing Nested Content Work 2017-06-05 09:28:52 +02:00
Jeavon Leopold 643dab0238 Merge branch 'feature/health-check-scheduler' of https://github.com/AndyButland/Umbraco-CMS into temp-U4-9984 2017-06-05 09:26:39 +02:00
AndyButland 1445ff7a83 Implemented scheduled task for notification on status of health checks 2017-06-05 09:21:35 +02:00
Lars-Erik Aabech b70241e807 Allowing blank documents configurable 2017-06-05 00:49:06 +02:00
Lars-Erik Aabech 10761a2211 Selecting blueprints made configurable 2017-06-05 00:39:16 +02:00
Lars-Erik Aabech f392c32d63 More localization and validation of blueprints 2017-06-05 00:20:02 +02:00
Lars-Erik Aabech 95cfb6c56b More localization of blueprint actions 2017-06-04 23:44:12 +02:00
Shannon d59e3d34f9 fix double submit 2017-06-04 23:42:33 +02:00
Shannon 1a03c7f068 Merge remote-tracking branch 'lars/temp-content-blueprints' into temp-content-blueprints
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/content/content.createblueprint.controller.js
#	src/Umbraco.Web.UI.Client/src/views/content/createblueprint.html
2017-06-04 23:40:39 +02:00
Shannon f52e82296f required validation and styling working 2017-06-04 23:35:53 +02:00
Lars-Erik Aabech 16868d7b51 Colors and cancelling 2017-06-04 23:04:10 +02:00
Lars-Erik Aabech 59df08f956 Merge remote-tracking branch 'origin/temp-content-blueprints' into temp-content-blueprints 2017-06-04 22:58:13 +02:00
Shannon 3c9041b00e adds refresh to content type node 2017-06-04 22:57:49 +02:00
Lars-Erik Aabech 09a39c1fe4 Right icon for "blueprint from content" action 2017-06-04 22:57:11 +02:00
Shannon 7377b7614e Renders blueprints underneath their associated document types, removes preview button 2017-06-04 22:52:18 +02:00
Shannon e2137c9a43 Gets updating blueprints working 2017-06-04 22:07:58 +02:00
Shannon f2b9963385 Merge remote-tracking branch 'lars/temp-content-blueprints' into temp-content-blueprints 2017-06-04 21:21:30 +02:00
Shannon f5ad4a35cd blueprint scaffolding working 2017-06-04 21:21:16 +02:00
Lars-Erik Aabech b55aa7c16d Filtering out properties 2017-06-04 21:20:42 +02:00
Lars-Erik Aabech ee577da48e Merge remote-tracking branch 'origin/temp-content-blueprints' into temp-content-blueprints 2017-06-04 21:01:31 +02:00
Lars-Erik Aabech affe3d688b English language elements for blueprints 2017-06-04 21:00:08 +02:00
Shannon addd8946e8 Merge remote-tracking branch 'lars/temp-content-blueprints' into temp-content-blueprints 2017-06-04 20:59:13 +02:00
Shannon d2fe3e7406 editing blueprints now working 2017-06-04 20:58:56 +02:00
Lars-Erik Aabech 6b0ef0e289 Disabling content editor tests to not "break the build" 2017-06-04 18:32:34 +02:00
Lars-Erik Aabech 7c0acff9f8 Naming and correct name in notification 2017-06-04 18:29:10 +02:00
Lars-Erik Aabech b2279a30f7 Can create docs without blueprint 2017-06-04 18:15:31 +02:00
Lars-Erik Aabech 22758c6e12 Merge remote-tracking branch 'origin/temp-content-blueprints' into temp-content-blueprints 2017-06-04 18:03:36 +02:00
Lars-Erik Aabech f8ed2a3db4 Blueprint dialogs done(ish) 2017-06-04 18:03:07 +02:00
Shannon c9f6778e31 Merge remote-tracking branch 'origin/dev-v7' into temp-content-blueprints 2017-06-04 17:57:15 +02:00
Shannon 494a7d54b8 Deleting works, wired up some validation for duplicate names 2017-06-04 17:57:06 +02:00
Lars-Erik Aabech 6dd9fc2780 Create dialog shows available blueprints and passes id to create page. 2017-06-04 17:49:21 +02:00
Shannon b99b9b7a6b adds blueprints dictionary to ContentTypeBasic result 2017-06-04 17:31:31 +02:00
Lars-Erik Aabech c06ac51b48 temp adding stubbed blueprints 2017-06-04 17:18:43 +02:00
Shannon abcd6e69f0 Merge remote-tracking branch 'lars/temp-content-blueprints' into temp-content-blueprints 2017-06-04 17:17:46 +02:00
Lars-Erik Aabech 5fe22a2231 Create blueprint from content integrated & working. 2017-06-04 17:14:12 +02:00
Shannon Deminick 03cb5bf9e5 Merge pull request #1979 from lars-erik/temp-content-blueprints
Temp content blueprints
2017-06-04 16:51:54 +02:00
Lars-Erik Aabech 79fa7db631 Merge remote-tracking branch 'origin/temp-content-blueprints' into temp-content-blueprints 2017-06-04 16:44:53 +02:00
Lars-Erik Aabech 4574bf2de2 Reverting inadvertent file changes 2017-06-04 16:37:33 +02:00
Shannon 3351a3e112 Merge remote-tracking branch 'origin/temp-content-blueprints' into temp-content-blueprints 2017-06-04 16:29:53 +02:00
Shannon 584c3e4c19 adds parameters to the content editor directive 2017-06-04 16:27:59 +02:00
Shannon Deminick 479f6a1525 Merge pull request #1977 from lars-erik/temp-content-blueprints
Temp content blueprints
2017-06-04 16:14:29 +02:00
Lars-Erik Aabech 6f55515f11 Merge branch 'temp-content-blueprints' of https://github.com/umbraco/Umbraco-CMS into temp-content-blueprints 2017-06-04 16:07:51 +02:00
Lars-Erik Aabech 9f0639ba24 Create blueprint from content action
In process of adding blueprints to create content dialog
2017-06-04 16:07:11 +02:00
Shannon 028903c306 Creates endpoint to create a blueprint 2017-06-04 16:00:25 +02:00
Shannon adb02c96a7 Gets blueprint tree controller working, makes the content editor a directive :O 2017-06-04 15:54:51 +02:00
Shannon f4d0de6665 get services/repos working for blueprints along with tests 2017-06-04 14:43:28 +02:00
Shannon 618c50f9da Ability to add/remove/replace an ISearchableTree 2017-05-31 22:52:16 +02:00
Shannon 106a06be2a Gets templates working 2017-05-31 13:09:26 +02:00
Shannon a70c9226fc Updates lots of trees for searching and gets results grouped 2017-05-31 12:25:05 +02:00
Mads Rasmussen f3763dffe5 prototype user picker 2017-05-30 17:04:41 +02:00
Mads Rasmussen be939b48f9 wire up users pagination 2017-05-30 11:57:41 +02:00
Mads Rasmussen b678eff484 wire up user state badges + create user confirmation 2017-05-30 11:31:43 +02:00
Mads Rasmussen 01ff16d00d add border to sidebar group 2017-05-30 10:12:44 +02:00
Mads Rasmussen a50f1e22c0 add progress bar to user avatar upload 2017-05-30 10:12:16 +02:00
Shannon 3ae16e717c Merge branch 'dev-v7-pluggable-ISearchableTree' of https://github.com/efabioli/Umbraco-CMS into efabioli-dev-v7-pluggable-ISearchableTree
# Conflicts:
#	src/Umbraco.Web/Editors/EntityController.cs
#	src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs
2017-05-30 00:48:41 +02:00
Shannon 06aaa9718f fixes test 2017-05-29 17:25:44 +02:00
Shannon d3eeb45651 ensures the message is added to the email 2017-05-29 15:57:59 +02:00
Shannon 6cd52bbc3c Gets user invite generating the token, sending the email and then verifying the token 2017-05-29 15:55:36 +02:00
Shannon 273c934e26 wires up the invite user to the rest endpoint 2017-05-29 15:13:04 +02:00
Shannon 2087125d95 Fixes an issue with model mappings and group assignments 2017-05-29 14:16:48 +02:00
Shannon 7f27745d27 starting to get the invite email with token working 2017-05-29 09:36:07 +02:00
Shannon f493c254e8 Fixes up the users login date on the user list view, fixes tests 2017-05-26 12:47:31 +10:00
Shannon 362b98509b Updates path check to use all assigned user start nodes that are combined by their groups and directly assigned values 2017-05-26 12:09:06 +10:00
Shannon 4337036358 refactor users and how groups are added to them so we can know dynamically start nodes, etc... fixes tests 2017-05-26 11:17:06 +10:00
Shannon 271c771f1c fixing tests 2017-05-26 09:50:51 +10:00
Mads Rasmussen 7934ea14a3 wire up enable and disable user 2017-05-25 22:09:52 +02:00
Shannon 8aabb3e3d0 wires up custom user avatars 2017-05-26 02:15:37 +10:00
Shannon 528be48437 Adds UsersController.SetAvatar and ClearAvatar 2017-05-26 00:02:32 +10:00
Shannon a8220e0f62 gets user saving working 2017-05-25 12:17:32 +10:00
Mads Rasmussen cb097f11a1 add save user and create user 2017-05-25 00:13:11 +02:00
Mads Rasmussen 911fdf20a4 add watcher to avatar name 2017-05-24 22:53:25 +02:00
Mads Rasmussen 939d128662 fix load indicator on user 2017-05-24 22:43:51 +02:00
Mads Rasmussen b95ba2e9a7 wire up user group picker 2017-05-24 22:36:44 +02:00
Mads Rasmussen e9c917e60b add resource for getUserGroups 2017-05-24 22:35:47 +02:00
Shannon 2cd23cb24f Updates business logic to persist the start nodes for users 2017-05-25 02:46:21 +10:00
Shannon 59872b0e17 fixes ability to create new users with the legacy editor 2017-05-25 02:11:38 +10:00
Shannon 6e9ae485ea Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-05-25 02:03:58 +10:00
Shannon dec9422531 Updated DB and business logic to support multiple starts nodes for a user and for user groups to have start nodes - U4-9915 User Groups will have a start node assigned and Users can have multiple start nodes assigned 2017-05-25 02:03:41 +10:00
Mads Rasmussen 633bd847ea Merge branch 'user-group-permissions' of https://github.com/umbraco/Umbraco-CMS into user-group-permissions 2017-05-24 11:32:55 +02:00
Shannon 38837049f0 adds invite user endpoint with a controller test! 2017-05-24 19:01:01 +10:00
Mads Rasmussen 1a637961b7 bump avatar font size 2017-05-23 20:57:43 +02:00
Mads Rasmussen f792be9495 wire up language selector 2017-05-23 15:31:51 +02:00
Mads Rasmussen 41460d5500 rename from role to group 2017-05-23 11:02:52 +02:00
Mads Rasmussen d56bee8761 Merge branch 'temp-U4-9827' into user-group-permissions
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js
#	src/Umbraco.Web.UI.Client/src/less/belle.less
2017-05-23 10:28:59 +02:00
Mads Rasmussen 4c852eab0b fix js error 2017-05-23 10:16:29 +02:00
Shannon 0cca342b83 updates user groups to contain the correct db columns for start nodes, updates mappings, fixes tests. 2017-05-23 15:48:08 +10:00
Shannon 45fa106c42 Adding required user group db columns and models, fixing tests 2017-05-23 15:23:10 +10:00
Shannon 6e9e2b99a4 U4-9944 User table needs to have created/update dates , adds more methods to the user controller for enabling/disabling users 2017-05-23 13:18:29 +10:00
Shannon 1be3f06bc4 Hides the user invite button if there is no email server configured 2017-05-23 12:28:41 +10:00
Shannon 86e4acef86 passes through the user id for the user editor 2017-05-23 12:13:01 +10:00
Mads Rasmussen b3118f742d increase breadcrumb font size 2017-05-22 14:25:57 +02:00
Mads Rasmussen 83d26fd91a add breadcrumb + back button to editor footer 2017-05-22 14:25:29 +02:00
Shannon 16c6fc70bf updates user resource to use the real endpoint 2017-05-22 21:26:46 +10:00
Shannon 517dc3abe3 updates more user properties 2017-05-22 21:23:04 +10:00
Mads Rasmussen 48432179be rename from user role to user group 2017-05-22 12:14:46 +02:00
Mads Rasmussen f3d925b509 wire up getPaged getPagedResults for users 2017-05-22 11:31:54 +02:00
Mads Rasmussen e590b20e75 remove some strokes and borders 2017-05-19 13:12:53 +02:00
Shannon 048112dcea Adds framework for testing our controllers (borrowed from the rest api project) adds a couple easy ones for the user controller 2017-05-19 20:17:50 +10:00
Shannon fce7adcc8e Merge branch 'dev-v7' into user-group-permissions 2017-05-19 13:21:27 +10:00
Shannon fcc71958e3 Merge remote-tracking branch 'origin/dev-v7' into user-group-permissions
# Conflicts:
#	.gitignore
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
#	src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx
2017-05-19 09:59:59 +10:00
Shannon bb431c7681 updates ignore 2017-05-19 09:57:53 +10:00
Mads Rasmussen aeabca87d7 init edit role editor 2017-05-18 15:25:05 +02:00
Mads Rasmussen fbed82219d init edit user editor 2017-05-18 15:23:38 +02:00
Mads Rasmussen d57a273c65 Add support for multiple styles on a button component 2017-05-18 13:33:57 +02:00
Mads Rasmussen d90ef2865b don't use bold for badges 2017-05-18 11:33:37 +02:00
Shannon 29177974eb adds the getPagedResults method to the usersResource 2017-05-18 16:38:14 +10:00
Shannon b130ccf9c1 removes userResource and adds the disableUser to the usersResource 2017-05-18 15:53:54 +10:00
Shannon 4c61cac9a8 rename to users controller 2017-05-17 22:58:32 +10:00
Shannon 57bd5178f7 Gets proper paging and filtering working for users 2017-05-17 21:40:17 +10:00
Mads Rasmussen ef51336a7e add text-transform to badge component 2017-05-17 12:12:49 +02:00
Mads Rasmussen d3dd84671f update mocked user object 2017-05-17 12:11:42 +02:00
Mads Rasmussen 5e3e9cce12 add setting for button sizes + allow icon on buttons 2017-05-17 12:10:50 +02:00
Shannon 9bb880f195 Starts getting the user service/repo to do paging filtering nicely 2017-05-17 19:47:16 +10:00
Mads Rasmussen c299edfe5a U4-9901 User role picker should show permission details 2017-05-16 15:08:49 +02:00
Mads Rasmussen 73d7915fb5 fixes: U4-9900 Add support for selecting an editor sub view trough route params 2017-05-16 13:14:29 +02:00
Shannon 225f657154 fixes tests for mappings 2017-05-16 18:18:10 +10:00
Shannon 4f80902ac2 Fixes unit tests - all but one 2017-05-16 17:17:02 +10:00
Shannon 26b8236eea fixes user query relator 2017-05-16 16:09:01 +10:00
Shannon ffd63a9103 fixes merge issues 2017-05-16 15:51:30 +10:00
Shannon bb436426a4 Merge remote-tracking branch 'origin/temp-U4-9776' into temp-U4-8632
# Conflicts:
#	src/Umbraco.Web.UI/config/trees.config
2017-05-16 15:19:45 +10:00
Shannon faa6a229e6 Updates UserGroupRepository to ensure that it persists things int he correct order and inside of a transaction, removes new code that is already obsolete, fixes tests 2017-05-15 20:27:40 +10:00
Mads Rasmussen 0e8b8e3aef set up some simple filtering of actions based on selection 2017-05-11 13:45:18 +02:00
Mads Rasmussen 7cf3f10afb missed two paths 2017-05-11 13:44:35 +02:00
Shannon 9b50d6d251 Gets a few tests fixed 2017-05-11 20:51:31 +10:00
Mads Rasmussen 6a4a84d8aa update files to match new tree path 2017-05-11 10:25:57 +02:00
Mads Rasmussen 89c4f7dffd Merge branch 'temp-U4-9776' of https://github.com/umbraco/Umbraco-CMS into temp-U4-9776 2017-05-11 09:54:34 +02:00
Mads Rasmussen 4e76b61251 rename section 2017-05-11 09:53:43 +02:00
Mads Rasmussen 03cce6e8f6 move badge + add title 2017-05-11 09:53:19 +02:00
Mads Rasmussen 956250004e add go back button to edit user 2017-05-11 09:48:16 +02:00
Mads Rasmussen 536be45bbf add go back button to edit role 2017-05-11 09:47:35 +02:00
Mads Rasmussen e5240575bd add bulk selection to user roles 2017-05-11 09:31:58 +02:00
Shannon 84c7f5046e Fixes the user group queries to be sorted since the relator requires this 2017-05-11 15:15:46 +10:00
Shannon d2d6b6654a Adds models and methods for the UserController 2017-05-11 13:11:41 +10:00
Mads Rasmussen 1dc30214cc remove margin from umb-users 2017-05-10 15:06:06 +02:00
Mads Rasmussen a84159a1ba setup scaffolding for edit user and user role 2017-05-10 15:05:34 +02:00
Mads Rasmussen 7bfe736e5e restructure so we can use subviews for users and user roles 2017-05-10 13:42:04 +02:00
Shannon 1ea06930f4 fixes some tests 2017-05-10 21:34:35 +10:00
Shannon 512f3237f0 larger refactor so we don't need to worry about 'loading' a users groups, it will all just be done, updated queries to fetch a user, it's groups and it's sections all at once, it might be a little heavy but we'll see. Adds/removes a few other methods that didn't seem right and uses group aliases for most things, adds Datbase unique indexes for group aliases and names (need to fix tests though) 2017-05-10 21:00:30 +10:00
Shannon f594006b95 fixes db table order 2017-05-10 18:07:39 +10:00
Shannon fc58c4f546 bumps version updates a few methods 2017-05-10 18:06:46 +10:00
Mads Rasmussen 645e28eefe add meta data to cards + set up create user form 2017-05-09 20:52:20 +02:00
Mads Rasmussen 71fdd1df85 remove red from clear button 2017-05-09 20:49:16 +02:00
Mads Rasmussen aa99657dea add table layout 2017-05-09 13:00:02 +02:00
Mads Rasmussen dfd240bd25 set up user group picker 2017-05-08 15:13:28 +02:00
Mads Rasmussen ff68f18a26 fix icon alignment in buttons 2017-05-08 14:37:50 +02:00
Mads Rasmussen b0b0f0056e remove save function 2017-05-08 14:20:20 +02:00
Mads Rasmussen f044c4dde1 add functionality for bulk actions 2017-05-08 14:16:02 +02:00
Mads Rasmussen 205a83dd3c add checkmark component 2017-05-08 14:13:51 +02:00
Mads Rasmussen 350bd75310 add filters, layout selector and invite/create user buttons 2017-05-08 10:56:21 +02:00
Mads Rasmussen b92eb9dcdd make users groups an array 2017-05-08 10:53:39 +02:00
Mads Rasmussen c0c1e2bc06 prototype user invite 2017-05-08 10:52:45 +02:00
Mads Rasmussen 971f92301f add pagination to users 2017-05-04 11:45:58 +02:00
Mads Rasmussen e7137e9cce first steps on user dashboard 2017-05-04 11:34:09 +02:00
Mads Rasmussen 7001e791f7 add umb-badge component 2017-05-04 11:29:36 +02:00
Mads Rasmussen e327a971c2 update umb-avatar component include name and colors 2017-05-04 11:25:20 +02:00
Mads Rasmussen c8813c8584 add id to user groups data 2017-05-04 11:23:38 +02:00
Mads Rasmussen 0bbef8cf94 add temp resource for hardcoded data 2017-05-04 11:22:04 +02:00
Shannon 8f69efe331 It builds! 2017-05-04 19:16:52 +10:00
Shannon 24e2547656 Merge branch 'u4-8632b' of https://github.com/AndyButland/Umbraco-CMS into AndyButland-u4-8632b
# Conflicts:
#	src/Umbraco.Core/Models/Rdbms/UserGroup2NodePermissionDto.cs
#	src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
#	src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs
#	src/Umbraco.Core/Persistence/Repositories/UserRepository.cs
#	src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs
#	src/Umbraco.Core/Persistence/RepositoryFactory.cs
#	src/Umbraco.Core/Services/ContentService.cs
#	src/Umbraco.Core/Services/SectionService.cs
#	src/Umbraco.Core/Services/UserService.cs
#	src/Umbraco.Core/Umbraco.Core.csproj
#	src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
#	src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs
#	src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs
#	src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs
#	src/Umbraco.Tests/Services/ContentServiceTests.cs
#	src/Umbraco.Tests/Umbraco.Tests.csproj
#	src/Umbraco.Web.UI/config/trees.config
#	src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx
#	src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
#	src/Umbraco.Web/Umbraco.Web.csproj
#	src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
2017-05-04 18:29:43 +10:00
Mads Rasmussen 53ef2e356a add new temp users tree 2017-05-03 09:30:29 +02:00
AndyButland 11b97c5291 Removed unnecessary null check 2017-04-20 09:45:48 +01:00
Jeavon ece73578c8 Tweaks to RelatedLinks - moved Content property from RelatedLinkBase to RelatedLink and populated Content property for non UDI RelatedLink editor 2017-04-04 12:08:10 +01:00
AndyButland f266c78915 Fixed issues with user creation and deletion 2016-12-05 22:26:09 +01:00
Shane Marsden 8664d3cd35 forgot to add file to commit 2016-11-03 17:03:16 +00:00
Shane Marsden 2363cdd17b Painstakingly went through every image file one by one, looking for references, checking if file looked v6ish, checking for constructed filenames etc 2016-11-03 17:00:36 +00:00
AndyButland d7aba9ce4a Fixed test failure reported on CI (3) 2016-10-30 16:10:00 +01:00
AndyButland ece61a45d8 Fixed test failure reported on CI (2) 2016-10-30 14:16:01 +01:00
AndyButland 8bb3685e5d Fixed test failure reported on CI 2016-10-30 13:53:43 +01:00
AndyButland 6560f38cb0 Fixed caching issues on editing node permissions 2016-10-30 11:58:36 +01:00
AndyButland f316b3f694 Fixed display of node permissions when editing for a group 2016-10-29 23:57:31 +02:00
AndyButland 311401d119 Ensured groups always loaded along with users 2016-10-29 23:57:04 +02:00
AndyButland 93c400a005 Fixed and added unit tests for permission checks 2016-10-29 23:10:00 +02:00
AndyButland 92010adce6 Permission checks on nodes for users and groups 2016-10-29 08:28:57 +02:00
AndyButland b1800305b8 Removed user types and migrated them to groups. Returned to compiling state. 2016-10-28 09:20:52 +02:00
AndyButland 14a0e3cca7 Migration for moving user types to user groups 2016-10-27 20:31:50 +02:00
AndyButland ebbbf11903 Further removal of references to user to app and node tables 2016-10-27 20:29:21 +02:00
AndyButland 2d9aeb3aa1 Removed relationship between users and applications and got back to a compiling state.
Set up editing screens for group/node permissions.
2016-10-27 18:11:46 +02:00
AndyButland cbb36a900a Management of user groups with assignment of users.
Migration for creation of user group tables and data amends for moving from permissions on users to those on groups.
2016-10-26 22:38:54 +02:00
2152 changed files with 35441 additions and 87533 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ root=true
[*]
end_of_line = lf
indent_style = space
indent_size = 2
indent_size = 4
trim_trailing_whitespace = true
[*.{cs,cshtml,csx,vb,vbx,vbhtml,fs,fsx,txt,ps1,sql}]
+1
View File
@@ -143,5 +143,6 @@ build/csharp-docs.zip
build/msbuild.log
.vs/
src/packages/
src/PrecompiledWeb/*
build/tools/
src/PrecompiledWeb/*
+4 -4
View File
@@ -21,9 +21,9 @@
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.1, 3.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[3.0.1, 4.0.0)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[3.0.1, 4.0.0)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[3.0.1, 4.0.0)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[3.1.0, 4.0.0)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[3.1.0, 4.0.0)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[3.1.0, 4.0.0)" />
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
<dependency id="HtmlAgilityPack" version="[1.4.9.5, 2.0.0)" />
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
@@ -40,7 +40,7 @@
<dependency id="semver" version="[1.1.2, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
</dependencies>
</metadata>
<files>
+6 -4
View File
@@ -15,6 +15,7 @@
<section name="BaseRestExtensions" type="Umbraco.Core.Configuration.BaseRest.BaseRestSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="FileSystemProviders" type="Umbraco.Core.Configuration.FileSystemProvidersSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="dashBoard" type="Umbraco.Core.Configuration.Dashboard.DashboardSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecks.HealthChecksSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</sectionGroup>
</configSections>
@@ -30,6 +31,7 @@
<BaseRestExtensions configSource="config\BaseRestExtensions.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<FileSystemProviders configSource="config\FileSystemProviders.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<HealthChecks configSource="config\HealthChecks.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
</umbracoConfiguration>
<FileSystemProviders xdt:Transform="Remove" />
@@ -368,19 +370,19 @@
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
+8 -7
View File
@@ -102,15 +102,16 @@
xdt:Transform="Remove" />
<!--Users-->
<add application="users" alias="users" title="Users" type="umbraco.loadUsers, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="0"
<add initialize="true" sortOrder="0" alias="users" application="users" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.UserTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="users" alias="userTypes"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="users" alias="userTypes" title="User Types" type="umbraco.cms.presentation.Trees.UserTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="1"
xdt:Transform="Remove" />
<add application="users" alias="userPermissions"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="users" alias="userPermissions" title="User Permissions" type="umbraco.cms.presentation.Trees.UserPermissions, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
xdt:Transform="Remove" />
<!--Members-->
<add initialize="true" sortOrder="0" alias="member" application="member" title="Members" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MemberTreeController, umbraco"
+2 -1
View File
@@ -1,2 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.5
7.7.0
beta
+4 -4
View File
@@ -16,19 +16,19 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.5")]
[assembly: AssemblyInformationalVersion("7.6.5")]
[assembly: AssemblyFileVersion("7.7.0")]
[assembly: AssemblyInformationalVersion("7.7.0-beta")]
+4 -2
View File
@@ -55,8 +55,10 @@ namespace Umbraco.Core.Cache
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string UserCacheKey = "UmbracoUser";
public const string UserPermissionsCacheKey = "UmbracoUserPermissions";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string UserGroupPermissionsCacheKey = "UmbracoUserGroupPermissions";
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string ContentTypeCacheKey = "UmbracoContentType";
@@ -0,0 +1,34 @@
using System;
namespace Umbraco.Core.CodeAnnotations
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal class ActionMetadataAttribute : Attribute
{
public string Category { get; private set; }
public string Name { get; private set; }
/// <summary>
/// Constructor used to assign a Category, since no name is assigned it will try to be translated from the language files based on the action's alias
/// </summary>
/// <param name="category"></param>
public ActionMetadataAttribute(string category)
{
if (string.IsNullOrWhiteSpace(category)) throw new ArgumentException("Value cannot be null or whitespace.", "category");
Category = category;
}
/// <summary>
/// Constructor used to assign an explicit name and category
/// </summary>
/// <param name="category"></param>
/// <param name="name"></param>
public ActionMetadataAttribute(string category, string name)
{
if (string.IsNullOrWhiteSpace(category)) throw new ArgumentException("Value cannot be null or whitespace.", "category");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
Category = category;
Name = name;
}
}
}
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Configuration;
using System.Web;
using System.Web.Configuration;
using System.Web.Hosting;
@@ -42,7 +43,6 @@ namespace Umbraco.Core.Configuration
//ensure the built on (non-changeable) reserved paths are there at all times
private const string StaticReservedPaths = "~/app_plugins/,~/install/,";
private const string StaticReservedUrls = "~/config/splashes/booting.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd,";
#endregion
/// <summary>
@@ -53,6 +53,7 @@ namespace Umbraco.Core.Configuration
_reservedUrlsCache = null;
_reservedPaths = null;
_reservedUrls = null;
HasSmtpServer = null;
}
/// <summary>
@@ -64,7 +65,26 @@ namespace Umbraco.Core.Configuration
ResetInternal();
}
/// <summary>
public static bool HasSmtpServerConfigured(string appPath)
{
if (HasSmtpServer.HasValue) return HasSmtpServer.Value;
var config = WebConfigurationManager.OpenWebConfiguration(appPath);
var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings");
if (settings == null || settings.Smtp == null) return false;
if (settings.Smtp.SpecifiedPickupDirectory != null && string.IsNullOrEmpty(settings.Smtp.SpecifiedPickupDirectory.PickupDirectoryLocation) == false)
return true;
if (settings.Smtp.Network != null && string.IsNullOrEmpty(settings.Smtp.Network.Host) == false)
return true;
return false;
}
/// <summary>
/// For testing only
/// </summary>
internal static bool? HasSmtpServer { get; set; }
/// <summary>
/// Gets the reserved urls from web.config.
/// </summary>
/// <value>The reserved urls.</value>
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
namespace Umbraco.Core.Configuration.HealthChecks
{
public class DisabledHealthCheckElement : ConfigurationElement, IDisabledHealthCheck
{
private const string IdKey = "id";
private const string DisabledOnKey = "disabledOn";
private const string DisabledByKey = "disabledBy";
[ConfigurationProperty(IdKey, IsKey = true, IsRequired = true)]
public Guid Id
{
get
{
return ((Guid)(base[IdKey]));
}
}
[ConfigurationProperty(DisabledOnKey, IsKey = false, IsRequired = false)]
public DateTime DisabledOn
{
get
{
return ((DateTime)(base[DisabledOnKey]));
}
}
[ConfigurationProperty(DisabledByKey, IsKey = false, IsRequired = false)]
public int DisabledBy
{
get
{
return ((int)(base[DisabledByKey]));
}
}
}
}
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.HealthChecks
{
[ConfigurationCollection(typeof(DisabledHealthCheckElement), AddItemName = "check")]
public class DisabledHealthChecksElementCollection : ConfigurationElementCollection, IEnumerable<IDisabledHealthCheck>
{
protected override ConfigurationElement CreateNewElement()
{
return new DisabledHealthCheckElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((DisabledHealthCheckElement)(element)).Id;
}
public new DisabledHealthCheckElement this[string key]
{
get
{
return (DisabledHealthCheckElement)BaseGet(key);
}
}
IEnumerator<IDisabledHealthCheck> IEnumerable<IDisabledHealthCheck>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return BaseGet(i) as DisabledHealthCheckElement;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.HealthChecks
{
public class HealthCheckNotificationSettingsElement : ConfigurationElement, IHealthCheckNotificationSettings
{
private const string EnabledKey = "enabled";
private const string FirstRunTimeKey = "firstRunTime";
private const string PeriodKey = "periodInHours";
private const string NotificationMethodsKey = "notificationMethods";
private const string DisabledChecksKey = "disabledChecks";
[ConfigurationProperty(EnabledKey, IsRequired = true)]
public bool Enabled
{
get
{
return (bool)base[EnabledKey];
}
}
[ConfigurationProperty(FirstRunTimeKey, IsRequired = false)]
public string FirstRunTime
{
get
{
return (string)base[FirstRunTimeKey];
}
}
[ConfigurationProperty(PeriodKey, IsRequired = true)]
public int PeriodInHours
{
get
{
return (int)base[PeriodKey];
}
}
[ConfigurationProperty(NotificationMethodsKey, IsDefaultCollection = true, IsRequired = false)]
public NotificationMethodsElementCollection NotificationMethods
{
get
{
return (NotificationMethodsElementCollection)base[NotificationMethodsKey];
}
}
[ConfigurationProperty(DisabledChecksKey, IsDefaultCollection = false, IsRequired = false)]
public DisabledHealthChecksElementCollection DisabledChecks
{
get
{
return (DisabledHealthChecksElementCollection)base[DisabledChecksKey];
}
}
bool IHealthCheckNotificationSettings.Enabled
{
get { return Enabled; }
}
string IHealthCheckNotificationSettings.FirstRunTime
{
get { return FirstRunTime; }
}
int IHealthCheckNotificationSettings.PeriodInHours
{
get { return PeriodInHours; }
}
IReadOnlyDictionary<string, INotificationMethod> IHealthCheckNotificationSettings.NotificationMethods
{
get { return NotificationMethods; }
}
IEnumerable<IDisabledHealthCheck> IHealthCheckNotificationSettings.DisabledChecks
{
get { return DisabledChecks; }
}
}
}
@@ -0,0 +1,8 @@
namespace Umbraco.Core.Configuration.HealthChecks
{
public enum HealthCheckNotificationVerbosity
{
Summary,
Detailed
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.HealthChecks
{
public class HealthChecksSection : ConfigurationSection, IHealthChecks
{
private const string DisabledChecksKey = "disabledChecks";
private const string NotificationSettingsKey = "notificationSettings";
[ConfigurationProperty(DisabledChecksKey)]
public DisabledHealthChecksElementCollection DisabledChecks
{
get { return ((DisabledHealthChecksElementCollection)(base[DisabledChecksKey])); }
}
[ConfigurationProperty(NotificationSettingsKey, IsRequired = true)]
public HealthCheckNotificationSettingsElement NotificationSettings
{
get { return ((HealthCheckNotificationSettingsElement)(base[NotificationSettingsKey])); }
}
IEnumerable<IDisabledHealthCheck> IHealthChecks.DisabledChecks
{
get { return DisabledChecks; }
}
IHealthCheckNotificationSettings IHealthChecks.NotificationSettings
{
get { return NotificationSettings; }
}
}
}
@@ -0,0 +1,11 @@
using System;
namespace Umbraco.Core.Configuration.HealthChecks
{
public interface IDisabledHealthCheck
{
Guid Id { get; }
DateTime DisabledOn { get; }
int DisabledBy { get; }
}
}
@@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Umbraco.Core.Configuration.HealthChecks
{
public interface IHealthCheckNotificationSettings
{
bool Enabled { get; }
string FirstRunTime { get; }
int PeriodInHours { get; }
IReadOnlyDictionary<string, INotificationMethod> NotificationMethods { get; }
IEnumerable<IDisabledHealthCheck> DisabledChecks { get; }
}
}
@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Umbraco.Core.Configuration.HealthChecks
{
public interface IHealthChecks
{
IEnumerable<IDisabledHealthCheck> DisabledChecks { get; }
IHealthCheckNotificationSettings NotificationSettings { get; }
}
}
@@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Umbraco.Core.Configuration.HealthChecks
{
public interface INotificationMethod
{
string Alias { get; }
bool Enabled { get; }
HealthCheckNotificationVerbosity Verbosity { get; }
bool FailureOnly { get; }
IReadOnlyDictionary<string, INotificationMethodSettings> Settings { get; }
}
}
@@ -0,0 +1,8 @@
namespace Umbraco.Core.Configuration.HealthChecks
{
public interface INotificationMethodSettings
{
string Key { get; }
string Value { get; }
}
}
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.HealthChecks
{
public class NotificationMethodElement : ConfigurationElement, INotificationMethod
{
private const string AliasKey = "alias";
private const string EnabledKey = "enabled";
private const string VerbosityKey = "verbosity";
private const string FailureonlyKey = "failureOnly";
private const string SettingsKey = "settings";
[ConfigurationProperty(AliasKey, IsKey = true, IsRequired = true)]
public string Alias
{
get
{
return (string)base[AliasKey];
}
}
[ConfigurationProperty(EnabledKey, IsKey = true, IsRequired = true)]
public bool Enabled
{
get
{
return (bool)base[EnabledKey];
}
}
[ConfigurationProperty(VerbosityKey, IsRequired = true)]
public HealthCheckNotificationVerbosity Verbosity
{
get
{
return (HealthCheckNotificationVerbosity)base[VerbosityKey];
}
}
[ConfigurationProperty(FailureonlyKey, IsRequired = false)]
public bool FailureOnly
{
get
{
return (bool)base[FailureonlyKey];
}
}
[ConfigurationProperty(SettingsKey, IsDefaultCollection = true, IsRequired = false)]
public NotificationMethodSettingsElementCollection Settings
{
get
{
return (NotificationMethodSettingsElementCollection)base[SettingsKey];
}
}
string INotificationMethod.Alias
{
get { return Alias; }
}
bool INotificationMethod.Enabled
{
get { return Enabled; }
}
HealthCheckNotificationVerbosity INotificationMethod.Verbosity
{
get { return Verbosity; }
}
bool INotificationMethod.FailureOnly
{
get { return FailureOnly; }
}
IReadOnlyDictionary<string, INotificationMethodSettings> INotificationMethod.Settings
{
get { return Settings; }
}
}
}
@@ -0,0 +1,28 @@
using System.Configuration;
namespace Umbraco.Core.Configuration.HealthChecks
{
public class NotificationMethodSettingsElement : ConfigurationElement, INotificationMethodSettings
{
private const string KeyKey = "key";
private const string ValueKey = "value";
[ConfigurationProperty(KeyKey, IsKey = true, IsRequired = true)]
public string Key
{
get
{
return (string)base[KeyKey];
}
}
[ConfigurationProperty(ValueKey, IsRequired = true)]
public string Value
{
get
{
return (string)base[ValueKey];
}
}
}
}
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
namespace Umbraco.Core.Configuration.HealthChecks
{
[ConfigurationCollection(typeof(NotificationMethodSettingsElement), AddItemName = "add")]
public class NotificationMethodSettingsElementCollection : ConfigurationElementCollection, IEnumerable<INotificationMethodSettings>, IReadOnlyDictionary<string, INotificationMethodSettings>
{
protected override ConfigurationElement CreateNewElement()
{
return new NotificationMethodSettingsElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((NotificationMethodSettingsElement)(element)).Key;
}
IEnumerator<KeyValuePair<string, INotificationMethodSettings>> IEnumerable<KeyValuePair<string, INotificationMethodSettings>>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
var val = (NotificationMethodSettingsElement)BaseGet(i);
var key = (string)BaseGetKey(i);
yield return new KeyValuePair<string, INotificationMethodSettings>(key, val);
}
}
IEnumerator<INotificationMethodSettings> IEnumerable<INotificationMethodSettings>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return (NotificationMethodSettingsElement)BaseGet(i);
}
}
bool IReadOnlyDictionary<string, INotificationMethodSettings>.ContainsKey(string key)
{
return ((IReadOnlyDictionary<string, INotificationMethodSettings>)this).Keys.Any(x => x == key);
}
bool IReadOnlyDictionary<string, INotificationMethodSettings>.TryGetValue(string key, out INotificationMethodSettings value)
{
try
{
var val = (NotificationMethodSettingsElement)BaseGet(key);
value = val;
return true;
}
catch (Exception)
{
value = null;
return false;
}
}
INotificationMethodSettings IReadOnlyDictionary<string, INotificationMethodSettings>.this[string key]
{
get { return (NotificationMethodSettingsElement)BaseGet(key); }
}
IEnumerable<string> IReadOnlyDictionary<string, INotificationMethodSettings>.Keys
{
get { return BaseGetAllKeys().Cast<string>(); }
}
IEnumerable<INotificationMethodSettings> IReadOnlyDictionary<string, INotificationMethodSettings>.Values
{
get
{
for (var i = 0; i < Count; i++)
{
yield return (NotificationMethodSettingsElement)BaseGet(i);
}
}
}
}
}
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
namespace Umbraco.Core.Configuration.HealthChecks
{
[ConfigurationCollection(typeof(NotificationMethodElement), AddItemName = "notificationMethod")]
public class NotificationMethodsElementCollection : ConfigurationElementCollection, IEnumerable<INotificationMethod>, IReadOnlyDictionary<string, INotificationMethod>
{
protected override ConfigurationElement CreateNewElement()
{
return new NotificationMethodElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((NotificationMethodElement)(element)).Alias;
}
IEnumerator<KeyValuePair<string, INotificationMethod>> IEnumerable<KeyValuePair<string, INotificationMethod>>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
var val = (NotificationMethodElement)BaseGet(i);
var key = (string)BaseGetKey(i);
yield return new KeyValuePair<string, INotificationMethod>(key, val);
}
}
IEnumerator<INotificationMethod> IEnumerable<INotificationMethod>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return (NotificationMethodElement)BaseGet(i);
}
}
bool IReadOnlyDictionary<string, INotificationMethod>.ContainsKey(string key)
{
return ((IReadOnlyDictionary<string, INotificationMethod>) this).Keys.Any(x => x == key);
}
bool IReadOnlyDictionary<string, INotificationMethod>.TryGetValue(string key, out INotificationMethod value)
{
try
{
var val = (NotificationMethodElement)BaseGet(key);
value = val;
return true;
}
catch (Exception)
{
value = null;
return false;
}
}
INotificationMethod IReadOnlyDictionary<string, INotificationMethod>.this[string key]
{
get { return (NotificationMethodElement)BaseGet(key); }
}
IEnumerable<string> IReadOnlyDictionary<string, INotificationMethod>.Keys
{
get { return BaseGetAllKeys().Cast<string>(); }
}
IEnumerable<INotificationMethod> IReadOnlyDictionary<string, INotificationMethod>.Values
{
get
{
for (var i = 0; i < Count; i++)
{
yield return (NotificationMethodElement)BaseGet(i);
}
}
}
}
}
@@ -9,6 +9,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Configuration.BaseRest;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.HealthChecks;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -52,6 +53,12 @@ namespace Umbraco.Core.Configuration
var dashboardConfig = ConfigurationManager.GetSection("umbracoConfiguration/dashBoard") as IDashboardSection;
SetDashboardSettings(dashboardConfig);
}
if (_healthChecks == null)
{
var healthCheckConfig = ConfigurationManager.GetSection("umbracoConfiguration/HealthChecks") as IHealthChecks;
SetHealthCheckSettings(healthCheckConfig);
}
}
/// <summary>
@@ -60,18 +67,36 @@ namespace Umbraco.Core.Configuration
/// <param name="umbracoSettings"></param>
/// <param name="baseRestSettings"></param>
/// <param name="dashboardSettings"></param>
public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings, IDashboardSection dashboardSettings)
/// <param name="healthChecks"></param>
public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings, IDashboardSection dashboardSettings, IHealthChecks healthChecks)
{
SetHealthCheckSettings(healthChecks);
SetUmbracoSettings(umbracoSettings);
SetBaseRestExtensions(baseRestSettings);
SetDashboardSettings(dashboardSettings);
}
private IHealthChecks _healthChecks;
private IDashboardSection _dashboardSection;
private IUmbracoSettingsSection _umbracoSettings;
private IBaseRestSection _baseRestExtensions;
private IGridConfig _gridConfig;
/// <summary>
/// Gets the IHealthCheck config
/// </summary>
public IHealthChecks HealthCheck()
{
if (_healthChecks == null)
{
var ex = new ConfigurationErrorsException("Could not load the " + typeof(IHealthChecks) + " from config file, ensure the web.config and healthchecks.config files are formatted correctly");
LogHelper.Error<UmbracoConfig>("Config error", ex);
throw ex;
}
return _healthChecks;
}
/// <summary>
/// Gets the IDashboardSection
/// </summary>
@@ -86,14 +111,23 @@ namespace Umbraco.Core.Configuration
return _dashboardSection;
}
/// <summary>
/// Only for testing
/// </summary>
/// <param name="value"></param>
public void SetDashboardSettings(IDashboardSection value)
{
_dashboardSection = value;
}
/// <summary>
/// Only for testing
/// </summary>
/// <param name="value"></param>
internal void SetDashboardSettings(IDashboardSection value)
public void SetHealthCheckSettings(IHealthChecks value)
{
_dashboardSection = value;
_healthChecks = value;
}
/// <summary>
@@ -1,8 +1,12 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This is no longer used and will be removed in future versions")]
internal class HelpElement : ConfigurationElement, IHelpSection
{
[ConfigurationProperty("defaultUrl", DefaultValue = "http://our.umbraco.org/wiki/umbraco-help/{0}/{1}")]
@@ -1,7 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
{
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This is no longer used and will be removed in future versions")]
public interface IHelpSection : IUmbracoConfigurationSection
{
string DefaultUrl { get; }
@@ -1,5 +1,10 @@
namespace Umbraco.Core.Configuration.UmbracoSettings
using System;
using System.ComponentModel;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This is no longer used and will be removed in future versions")]
public interface ILink
{
string Application { get; }
@@ -1,4 +1,7 @@
namespace Umbraco.Core.Configuration.UmbracoSettings
using System;
using System.ComponentModel;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
public interface IUmbracoSettingsSection : IUmbracoConfigurationSection
{
@@ -24,6 +27,8 @@
IProvidersSection Providers { get; }
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This is no longer used and will be removed in future versions")]
IHelpSection Help { get; }
IWebRoutingSection WebRouting { get; }
@@ -1,7 +1,11 @@
using System.Configuration;
using System;
using System.ComponentModel;
using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This is no longer used and will be removed in future versions")]
internal class LinkElement : ConfigurationElement, ILink
{
[ConfigurationProperty("application")]
@@ -1,8 +1,12 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This is no longer used and will be removed in future versions")]
internal class LinksCollection : ConfigurationElementCollection, IEnumerable<ILink>
{
protected override ConfigurationElement CreateNewElement()
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
@@ -183,6 +184,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return Providers; }
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This is no longer used and will be removed in future versions")]
IHelpSection IUmbracoSettingsSection.Help
{
get { return Help; }
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.6.5");
private static readonly Version Version = new Version("7.7.0");
/// <summary>
/// Gets the current version of Umbraco.
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return ""; } }
public static string CurrentComment { get { return "beta"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
+8 -1
View File
@@ -56,7 +56,12 @@
/// <summary>
/// alias for the content tree.
/// </summary>
public const string Content = "content";
public const string Content = "content";
/// <summary>
/// alias for the content blueprint tree.
/// </summary>
public const string ContentBlueprints = "contentBlueprints";
/// <summary>
/// alias for the member tree.
@@ -118,6 +123,8 @@
public const string Scripts = "scripts";
public const string Users = "users";
//TODO: Fill in the rest!
}
}
@@ -12,6 +12,14 @@ namespace Umbraco.Core
/// </summary>
public static class Conventions
{
internal static class PermissionCategories
{
public const string ContentCategory = "content";
public const string AdministrationCategory = "administration";
public const string StructureCategory = "structure";
public const string OtherCategory = "other";
}
public static class PublicAccess
{
public const string MemberUsernameRuleType = "MemberUsername";
+11 -1
View File
@@ -68,12 +68,22 @@ namespace Umbraco.Core
/// Guid for a Document object.
/// </summary>
public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
/// <summary>
/// Guid for a Document object.
/// </summary>
public static readonly Guid DocumentGuid = new Guid(Document);
/// <summary>
/// Guid for a Document Blueprint object.
/// </summary>
public const string DocumentBlueprint = "6EBEF410-03AA-48CF-A792-E1C1CB087ACA";
/// <summary>
/// Guid for a Document object.
/// </summary>
public static readonly Guid DocumentBlueprintGuid = new Guid(DocumentBlueprint);
/// <summary>
/// Guid for a Document Type object.
/// </summary>
@@ -437,6 +437,11 @@ namespace Umbraco.Core
/// </summary>
public const string EmailAddressAlias = "Umbraco.EmailAddress";
/// <summary>
/// Alias for the nested content property editor.
/// </summary>
public const string NestedContentAlias = "Umbraco.NestedContent";
public static class PreValueKeys
{
/// <summary>
+5
View File
@@ -8,12 +8,17 @@ namespace Umbraco.Core
public static class Security
{
public const string AdminGroupAlias = "admin";
public const string BackOfficeAuthenticationType = "UmbracoBackOffice";
public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie";
public const string BackOfficeExternalCookieName = "UMB_EXTLOGIN";
public const string BackOfficeTokenAuthenticationType = "UmbracoBackOfficeToken";
public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie";
internal const string EmptyPasswordPrefix = "___UIDEMPTYPWORD__";
internal const string ForceReAuthFlag = "umbraco-force-auth";
/// <summary>
/// The prefix used for external identity providers for their authentication type
/// </summary>
+37 -1
View File
@@ -41,7 +41,43 @@ namespace Umbraco.Core
Hour,
Minute,
Second
}
/// <summary>
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
/// date time is before the time, calculate onto next day)
/// </summary>
/// <param name="fromDateTime">Date to start from</param>
/// <param name="scheduledTime">Time to compare against (in Hmm form, e.g. 330, 2200)</param>
/// <returns></returns>
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
{
// Ensure time provided is 4 digits long
if (scheduledTime.Length == 3)
{
scheduledTime = "0" + scheduledTime;
}
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
DateTime scheduledDateTime;
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
{
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
}
else
{
var nextDay = fromDateTime.AddDays(1);
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
}
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
}
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
{
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
}
}
}
+3
View File
@@ -111,6 +111,9 @@ namespace Umbraco.Core
/// <returns></returns>
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other)
{
if (source == null) throw new ArgumentNullException("source");
if (other == null) throw new ArgumentNullException("other");
return other.Except(source).Any() == false;
}
+15 -2
View File
@@ -217,8 +217,21 @@ namespace Umbraco.Core
public static MemberInfo GetMemberInfo<T, TReturn>(Expression<Func<T, TReturn>> fromExpression)
{
if (fromExpression == null) return null;
var body = fromExpression.Body as MemberExpression;
return body != null ? body.Member : null;
MemberExpression me;
switch (fromExpression.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = fromExpression.Body as UnaryExpression;
me = ((ue != null) ? ue.Operand : null) as MemberExpression;
break;
default:
me = fromExpression.Body as MemberExpression;
break;
}
return me != null ? me.Member : null;
}
/// <summary>
+8 -18
View File
@@ -1,8 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
@@ -23,7 +20,7 @@ namespace Umbraco.Core.Models
private DateTime? _expireDate;
private int _writer;
private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name.
private bool _permissionsChanged;
/// <summary>
/// Constructor for creating a Content object
/// </summary>
@@ -32,7 +29,7 @@ namespace Umbraco.Core.Models
/// <param name="contentType">ContentType for the current Content object</param>
public Content(string name, IContent parent, IContentType contentType)
: this(name, parent, contentType, new PropertyCollection())
{
{
}
/// <summary>
@@ -68,7 +65,7 @@ namespace Umbraco.Core.Models
/// <param name="parentId">Id of the Parent content</param>
/// <param name="contentType">ContentType for the current Content object</param>
/// <param name="properties">Collection of properties</param>
public Content(string name, int parentId, IContentType contentType, PropertyCollection properties)
public Content(string name, int parentId, IContentType contentType, PropertyCollection properties)
: base(name, parentId, contentType, properties)
{
Mandate.ParameterNotNull(contentType, "contentType");
@@ -87,7 +84,6 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ExpireDate);
public readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo<Content, int>(x => x.WriterId);
public readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo<Content, string>(x => x.NodeName);
public readonly PropertyInfo PermissionsChangedSelector = ExpressionHelper.GetPropertyInfo<Content, bool>(x => x.PermissionsChanged);
}
/// <summary>
@@ -95,7 +91,7 @@ namespace Umbraco.Core.Models
/// This is used to override the default one from the ContentType.
/// </summary>
/// <remarks>
/// If no template is explicitly set on the Content object,
/// If no template is explicitly set on the Content object,
/// the Default template from the ContentType will be returned.
/// </remarks>
[DataMember]
@@ -197,15 +193,6 @@ namespace Umbraco.Core.Models
set { SetPropertyValueAndDetectChanges(value, ref _nodeName, Ps.Value.NodeNameSelector); }
}
/// <summary>
/// Used internally to track if permissions have been changed during the saving process for this entity
/// </summary>
[IgnoreDataMember]
internal bool PermissionsChanged
{
get { return _permissionsChanged; }
set { SetPropertyValueAndDetectChanges(value, ref _permissionsChanged, Ps.Value.PermissionsChangedSelector); }
}
/// <summary>
/// Gets the ContentType used by this content object
@@ -277,6 +264,9 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
internal DateTime PublishedDate { get; set; }
[DataMember]
public bool IsBlueprint { get; internal set; }
/// <summary>
/// Changes the Trashed state of the content object
/// </summary>
@@ -293,7 +283,7 @@ namespace Umbraco.Core.Models
ChangePublishedState(PublishedState.Unpublished);
}
}
/// <summary>
/// Method to call when Entity is being updated
/// </summary>
+6 -4
View File
@@ -54,6 +54,11 @@ namespace Umbraco.Core.Models
{
public readonly PropertyInfo DefaultTemplateSelector = ExpressionHelper.GetPropertyInfo<ContentType, int>(x => x.DefaultTemplateId);
public readonly PropertyInfo AllowedTemplatesSelector = ExpressionHelper.GetPropertyInfo<ContentType, IEnumerable<ITemplate>>(x => x.AllowedTemplates);
//Custom comparer for enumerable
public readonly DelegateEqualityComparer<IEnumerable<ITemplate>> TemplateComparer = new DelegateEqualityComparer<IEnumerable<ITemplate>>(
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
templates => templates.GetHashCode());
}
/// <summary>
@@ -91,10 +96,7 @@ namespace Umbraco.Core.Models
set
{
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<ITemplate>>(
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
templates => templates.GetHashCode()));
Ps.Value.TemplateComparer);
}
}
+9 -6
View File
@@ -88,6 +88,12 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, PropertyGroupCollection>(x => x.PropertyGroups);
public readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, IEnumerable<PropertyType>>(x => x.PropertyTypes);
public readonly PropertyInfo HasPropertyTypeBeenRemovedSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, bool>(x => x.HasPropertyTypeBeenRemoved);
//Custom comparer for enumerable
public readonly DelegateEqualityComparer<IEnumerable<ContentTypeSort>> ContentTypeSortComparer =
new DelegateEqualityComparer<IEnumerable<ContentTypeSort>>(
(sorts, enumerable) => sorts.UnsortedSequenceEqual(enumerable),
sorts => sorts.GetHashCode());
}
@@ -254,7 +260,7 @@ namespace Umbraco.Core.Models
set { SetPropertyValueAndDetectChanges(value, ref _trashed, Ps.Value.TrashedSelector); }
}
private IDictionary<string, object> _additionalData;
private readonly IDictionary<string, object> _additionalData;
/// <summary>
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
/// </summary>
@@ -273,11 +279,8 @@ namespace Umbraco.Core.Models
get { return _allowedContentTypes; }
set
{
SetPropertyValueAndDetectChanges(value, ref _allowedContentTypes, Ps.Value.AllowedContentTypesSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<ContentTypeSort>>(
(sorts, enumerable) => sorts.UnsortedSequenceEqual(enumerable),
sorts => sorts.GetHashCode()));
SetPropertyValueAndDetectChanges(value, ref _allowedContentTypes, Ps.Value.AllowedContentTypesSelector,
Ps.Value.ContentTypeSortComparer);
}
}
+7 -4
View File
@@ -37,6 +37,12 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, Guid?>(x => x.ParentId);
public readonly PropertyInfo ItemKeySelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, string>(x => x.ItemKey);
public readonly PropertyInfo TranslationsSelector = ExpressionHelper.GetPropertyInfo<DictionaryItem, IEnumerable<IDictionaryTranslation>>(x => x.Translations);
//Custom comparer for enumerable
public readonly DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>> DictionaryTranslationComparer =
new DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>>(
(enumerable, translations) => enumerable.UnsortedSequenceEqual(translations),
enumerable => enumerable.GetHashCode());
}
/// <summary>
@@ -79,10 +85,7 @@ namespace Umbraco.Core.Models
}
SetPropertyValueAndDetectChanges(asArray, ref _translations, Ps.Value.TranslationsSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>>(
(enumerable, translations) => enumerable.UnsortedSequenceEqual(translations),
enumerable => enumerable.GetHashCode()));
Ps.Value.DictionaryTranslationComparer);
}
}
}
+5 -1
View File
@@ -119,7 +119,7 @@ namespace Umbraco.Core.Models.EntityBase
if (IsPropertyDirty("CreateDate") == false || _createDate == default(DateTime))
CreateDate = DateTime.Now;
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default(DateTime))
UpdateDate = CreateDate;
UpdateDate = DateTime.Now;
}
/// <summary>
@@ -129,6 +129,10 @@ namespace Umbraco.Core.Models.EntityBase
{
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default(DateTime))
UpdateDate = DateTime.Now;
//this is just in case
if (_createDate == default(DateTime))
CreateDate = DateTime.Now;
}
/// <summary>
@@ -0,0 +1,8 @@
namespace Umbraco.Core.Models.EntityBase
{
public class EntityPath
{
public int Id { get; set; }
public string Path { get; set; }
}
}
+5 -2
View File
@@ -1,7 +1,5 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models
{
@@ -85,5 +83,10 @@ namespace Umbraco.Core.Models
/// Gets the unique identifier of the published version, if any.
/// </summary>
Guid PublishedVersionGuid { get; }
/// <summary>
/// Gets a value indicating whether the content item is a blueprint.
/// </summary>
bool IsBlueprint { get; }
}
}
@@ -2,23 +2,69 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
namespace Umbraco.Core.Models.Identity
{
public class BackOfficeIdentityUser : IdentityUser<int, IIdentityUserLogin, IdentityUserRole<string>, IdentityUserClaim<int>>
public class BackOfficeIdentityUser : IdentityUser<int, IIdentityUserLogin, IdentityUserRole<string>, IdentityUserClaim<int>>, IRememberBeingDirty
{
public BackOfficeIdentityUser()
/// <summary>
/// Used to construct a new instance without an identity
/// </summary>
/// <param name="username"></param>
/// <param name="email">This is allowed to be null (but would need to be filled in if trying to persist this instance)</param>
/// <param name="culture"></param>
/// <returns></returns>
public static BackOfficeIdentityUser CreateNew(string username, string email, string culture)
{
StartMediaId = -1;
StartContentId = -1;
Culture = Configuration.GlobalSettings.DefaultUILanguage;
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", "username");
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value cannot be null or whitespace.", "culture");
var user = new BackOfficeIdentityUser();
user.DisableChangeTracking();
user._userName = username;
user._email = email;
//we are setting minvalue here because the default is "0" which is the id of the admin user
//which we cannot allow because the admin user will always exist
user._id = int.MinValue;
user._hasIdentity = false;
user._culture = culture;
user.EnableChangeTracking();
return user;
}
private BackOfficeIdentityUser()
{
}
/// <summary>
/// Creates an existing user with the specified groups
/// </summary>
/// <param name="userId"></param>
/// <param name="groups"></param>
public BackOfficeIdentityUser(int userId, IEnumerable<IReadOnlyUserGroup> groups)
{
_startMediaIds = new int[] { };
_startContentIds = new int[] { };
_groups = new IReadOnlyUserGroup[] { };
_allowedSections = new string[] { };
_culture = Configuration.GlobalSettings.DefaultUILanguage;
_groups = groups.ToArray();
_roles = new ObservableCollection<IdentityUserRole<string>>(_groups.Select(x => new IdentityUserRole<string>
{
RoleId = x.Alias,
UserId = userId.ToString()
}));
_roles.CollectionChanged += _roles_CollectionChanged;
}
public virtual async Task<ClaimsIdentity> GenerateUserIdentityAsync(BackOfficeUserManager<BackOfficeIdentityUser> manager)
{
// NOTE the authenticationType must match the umbraco one
@@ -27,16 +73,155 @@ namespace Umbraco.Core.Models.Identity
return userIdentity;
}
/// <summary>
/// Returns true if an Id has been set on this object this will be false if the object is new and not peristed to the database
/// </summary>
public bool HasIdentity
{
get { return _hasIdentity; }
}
public int[] CalculatedMediaStartNodeIds { get; internal set; }
public int[] CalculatedContentStartNodeIds { get; internal set; }
public override int Id
{
get { return _id; }
set
{
_id = value;
_hasIdentity = true;
}
}
/// <summary>
/// Override Email so we can track changes to it
/// </summary>
public override string Email
{
get { return _email; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _email, Ps.Value.EmailSelector); }
}
/// <summary>
/// Override UserName so we can track changes to it
/// </summary>
public override string UserName
{
get { return _userName; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _userName, Ps.Value.UserNameSelector); }
}
/// <summary>
/// Override LastLoginDateUtc so we can track changes to it
/// </summary>
public override DateTime? LastLoginDateUtc
{
get { return _lastLoginDateUtc; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _lastLoginDateUtc, Ps.Value.LastLoginDateUtcSelector); }
}
/// <summary>
/// Override EmailConfirmed so we can track changes to it
/// </summary>
public override bool EmailConfirmed
{
get { return _emailConfirmed; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _emailConfirmed, Ps.Value.EmailConfirmedSelector); }
}
/// <summary>
/// Gets/sets the user's real name
/// </summary>
public string Name { get; set; }
public int StartContentId { get; set; }
public int StartMediaId { get; set; }
public string[] AllowedSections { get; set; }
public string Culture { get; set; }
public string Name
{
get { return _name; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
}
public string UserTypeAlias { get; set; }
/// <summary>
/// Override AccessFailedCount so we can track changes to it
/// </summary>
public override int AccessFailedCount
{
get { return _accessFailedCount; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _accessFailedCount, Ps.Value.AccessFailedCountSelector); }
}
/// <summary>
/// Override PasswordHash so we can track changes to it
/// </summary>
public override string PasswordHash
{
get { return _passwordHash; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _passwordHash, Ps.Value.PasswordHashSelector); }
}
/// <summary>
/// Content start nodes assigned to the User (not ones assigned to the user's groups)
/// </summary>
public int[] StartContentIds
{
get { return _startContentIds; }
set
{
if (value == null) value = new int[0];
_tracker.SetPropertyValueAndDetectChanges(value, ref _startContentIds, Ps.Value.StartContentIdsSelector, Ps.Value.StartIdsComparer);
}
}
/// <summary>
/// Media start nodes assigned to the User (not ones assigned to the user's groups)
/// </summary>
public int[] StartMediaIds
{
get { return _startMediaIds; }
set
{
if (value == null) value = new int[0];
_tracker.SetPropertyValueAndDetectChanges(value, ref _startMediaIds, Ps.Value.StartMediaIdsSelector, Ps.Value.StartIdsComparer);
}
}
/// <summary>
/// This is a readonly list of the user's allowed sections which are based on it's user groups
/// </summary>
public string[] AllowedSections
{
get { return _allowedSections ?? (_allowedSections = _groups.SelectMany(x => x.AllowedSections).Distinct().ToArray()); }
}
public string Culture
{
get { return _culture; }
set { _tracker.SetPropertyValueAndDetectChanges(value, ref _culture, Ps.Value.CultureSelector); }
}
public IReadOnlyUserGroup[] Groups
{
get { return _groups; }
set
{
//so they recalculate
_allowedSections = null;
//now clear all roles and re-add them
_roles.CollectionChanged -= _roles_CollectionChanged;
_roles.Clear();
foreach (var identityUserRole in _groups.Select(x => new IdentityUserRole<string>
{
RoleId = x.Alias,
UserId = Id.ToString()
}))
{
_roles.Add(identityUserRole);
}
_roles.CollectionChanged += _roles_CollectionChanged;
_tracker.SetPropertyValueAndDetectChanges(value, ref _groups, Ps.Value.GroupsSelector, Ps.Value.GroupsComparer);
}
}
/// <summary>
/// Lockout is always enabled
@@ -44,7 +229,7 @@ namespace Umbraco.Core.Models.Identity
public override bool LockoutEnabled
{
get { return true; }
set
set
{
//do nothing
}
@@ -82,16 +267,42 @@ namespace Umbraco.Core.Models.Identity
return _logins;
}
}
public bool LoginsChanged { get; private set; }
void Logins_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
LoginsChanged = true;
_tracker.OnPropertyChanged(Ps.Value.LoginsSelector);
}
private ObservableCollection<IIdentityUserLogin> _logins;
private Lazy<IEnumerable<IIdentityUserLogin>> _getLogins;
private void _roles_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
_tracker.OnPropertyChanged(Ps.Value.RolesSelector);
}
private readonly ObservableCollection<IdentityUserRole<string>> _roles;
/// <summary>
/// helper method to easily add a role without having to deal with IdentityUserRole{T}
/// </summary>
/// <param name="role"></param>
/// <remarks>
/// Adding a role this way will not reflect on the user's group's collection or it's allowed sections until the user is persisted
/// </remarks>
public void AddRole(string role)
{
Roles.Add(new IdentityUserRole<string>
{
UserId = this.Id.ToString(),
RoleId = role
});
}
/// <summary>
/// Override Roles because the value of these are the user's group aliases
/// </summary>
public override ICollection<IdentityUserRole<string>> Roles
{
get { return _roles; }
}
/// <summary>
/// Used to set a lazy call back to populate the user's Login list
@@ -101,6 +312,128 @@ namespace Umbraco.Core.Models.Identity
{
if (callback == null) throw new ArgumentNullException("callback");
_getLogins = callback;
}
#region Change tracking
public void DisableChangeTracking()
{
_tracker.DisableChangeTracking();
}
public void EnableChangeTracking()
{
_tracker.EnableChangeTracking();
}
/// <summary>
/// Since this class only has change tracking turned on for Email/Username this will return true if either of those have changed
/// </summary>
/// <returns></returns>
public bool IsDirty()
{
return _tracker.IsDirty();
}
/// <summary>
/// Returns true if the specified property is dirty
/// </summary>
/// <param name="propName"></param>
/// <returns></returns>
public bool IsPropertyDirty(string propName)
{
return _tracker.IsPropertyDirty(propName);
}
/// <summary>
/// Resets dirty properties
/// </summary>
void ICanBeDirty.ResetDirtyProperties()
{
_tracker.ResetDirtyProperties();
}
bool IRememberBeingDirty.WasDirty()
{
return _tracker.WasDirty();
}
bool IRememberBeingDirty.WasPropertyDirty(string propertyName)
{
return _tracker.WasPropertyDirty(propertyName);
}
void IRememberBeingDirty.ForgetPreviouslyDirtyProperties()
{
_tracker.ForgetPreviouslyDirtyProperties();
}
public void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
{
_tracker.ResetDirtyProperties(rememberPreviouslyChangedProperties);
}
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
private class PropertySelectors
{
public readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.Email);
public readonly PropertyInfo UserNameSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.UserName);
public readonly PropertyInfo LastLoginDateUtcSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, DateTime?>(x => x.LastLoginDateUtc);
public readonly PropertyInfo EmailConfirmedSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, bool>(x => x.EmailConfirmed);
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.Name);
public readonly PropertyInfo AccessFailedCountSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, int>(x => x.AccessFailedCount);
public readonly PropertyInfo PasswordHashSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.PasswordHash);
public readonly PropertyInfo CultureSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, string>(x => x.Culture);
public readonly PropertyInfo StartMediaIdsSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, int[]>(x => x.StartMediaIds);
public readonly PropertyInfo StartContentIdsSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, int[]>(x => x.StartContentIds);
public readonly PropertyInfo GroupsSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, IReadOnlyUserGroup[]>(x => x.Groups);
public readonly PropertyInfo LoginsSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, IEnumerable<IIdentityUserLogin>>(x => x.Logins);
public readonly PropertyInfo RolesSelector = ExpressionHelper.GetPropertyInfo<BackOfficeIdentityUser, IEnumerable<IdentityUserRole<string>>>(x => x.Roles);
//Custom comparer for enumerables
public readonly DelegateEqualityComparer<IReadOnlyUserGroup[]> GroupsComparer = new DelegateEqualityComparer<IReadOnlyUserGroup[]>(
(groups, enumerable) => groups.Select(x => x.Alias).UnsortedSequenceEqual(enumerable.Select(x => x.Alias)),
groups => groups.GetHashCode());
public readonly DelegateEqualityComparer<int[]> StartIdsComparer = new DelegateEqualityComparer<int[]>(
(groups, enumerable) => groups.UnsortedSequenceEqual(enumerable),
groups => groups.GetHashCode());
}
private readonly ChangeTracker _tracker = new ChangeTracker();
private string _email;
private string _userName;
private int _id;
private bool _hasIdentity = false;
private DateTime? _lastLoginDateUtc;
private bool _emailConfirmed;
private string _name;
private int _accessFailedCount;
private string _passwordHash;
private string _culture;
private ObservableCollection<IIdentityUserLogin> _logins;
private Lazy<IEnumerable<IIdentityUserLogin>> _getLogins;
private IReadOnlyUserGroup[] _groups;
private string[] _allowedSections;
private int[] _startMediaIds;
private int[] _startContentIds;
/// <summary>
/// internal class used to track changes for properties that have it enabled
/// </summary>
private class ChangeTracker : TracksChangesEntityBase
{
/// <summary>
/// Make this public so that it's usable
/// </summary>
/// <param name="propertyInfo"></param>
public new void OnPropertyChanged(PropertyInfo propertyInfo)
{
base.OnPropertyChanged(propertyInfo);
}
}
#endregion
}
}
@@ -1,7 +1,7 @@
using System;
using System.Linq;
using AutoMapper;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
@@ -13,28 +13,42 @@ namespace Umbraco.Core.Models.Identity
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
{
config.CreateMap<IUser, BackOfficeIdentityUser>()
.BeforeMap((user, identityUser) =>
{
identityUser.DisableChangeTracking();
})
.ConstructUsing(user => new BackOfficeIdentityUser(user.Id, user.Groups))
.ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime()))
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
.ForMember(user => user.EmailConfirmed, expression => expression.MapFrom(user => user.EmailConfirmedDate.HasValue))
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
.ForMember(user => user.UserName, expression => expression.MapFrom(user => user.Username))
.ForMember(user => user.PasswordHash, expression => expression.MapFrom(user => GetPasswordHash(user.RawPasswordValue)))
.ForMember(user => user.Culture, expression => expression.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
.ForMember(user => user.Name, expression => expression.MapFrom(user => user.Name))
.ForMember(user => user.StartMediaId, expression => expression.MapFrom(user => user.StartMediaId))
.ForMember(user => user.StartContentId, expression => expression.MapFrom(user => user.StartContentId))
.ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias))
.ForMember(user => user.StartMediaIds, expression => expression.MapFrom(user => user.StartMediaIds))
.ForMember(user => user.StartContentIds, expression => expression.MapFrom(user => user.StartContentIds))
.ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts))
.ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray()));
.ForMember(user => user.CalculatedContentStartNodeIds, expression => expression.MapFrom(user => user.CalculateContentStartNodeIds(applicationContext.Services.EntityService)))
.ForMember(user => user.CalculatedMediaStartNodeIds, expression => expression.MapFrom(user => user.CalculateMediaStartNodeIds(applicationContext.Services.EntityService)))
.ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray()))
.AfterMap((user, identityUser) =>
{
identityUser.ResetDirtyProperties(true);
identityUser.EnableChangeTracking();
});
config.CreateMap<BackOfficeIdentityUser, UserData>()
.ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
.ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Roles.Select(x => x.RoleId).ToArray()))
.ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias }))
.ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId))
.ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId))
//When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups
.ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.CalculatedContentStartNodeIds))
//When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups
.ForMember(detail => detail.StartMediaNodes, opt => opt.MapFrom(user => user.CalculatedMediaStartNodeIds))
.ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName))
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture))
.ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp));
@@ -42,7 +56,7 @@ namespace Umbraco.Core.Models.Identity
private string GetPasswordHash(string storedPass)
{
return storedPass.StartsWith("___UIDEMPTYPWORD__") ? null : storedPass;
return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass;
}
}
}
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents an <see cref="IContent"/> -> user group & permission key value pair collection
/// </summary>
/// <remarks>
/// This implements <see cref="IAggregateRoot"/> purely so it can be used with the repository layer which is why it's explicitly implemented.
/// </remarks>
public class ContentPermissionSet : EntityPermissionSet, IAggregateRoot
{
private readonly IContent _content;
public ContentPermissionSet(IContent content, EntityPermissionCollection permissionsSet)
: base(content.Id, permissionsSet)
{
_content = content;
}
public override int EntityId
{
get { return _content.Id; }
}
#region Explicit implementation of IAggregateRoot
int IEntity.Id
{
get { return EntityId; }
set { throw new NotImplementedException(); }
}
bool IEntity.HasIdentity
{
get { return EntityId > 0; }
}
Guid IEntity.Key { get; set; }
DateTime IEntity.CreateDate { get; set; }
DateTime IEntity.UpdateDate { get; set; }
DateTime? IDeletableEntity.DeletedDate { get; set; }
object IDeepCloneable.DeepClone()
{
throw new NotImplementedException();
}
#endregion
}
}
@@ -1,26 +1,66 @@
using System.Collections;
using System;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents a user -> entity permission
/// Represents an entity permission (defined on the user group and derived to retrieve permissions for a given user)
/// </summary>
public class EntityPermission
public class EntityPermission : IEquatable<EntityPermission>
{
public EntityPermission(int userId, int entityId, string[] assignedPermissions)
public EntityPermission(int groupId, int entityId, string[] assignedPermissions)
{
UserId = userId;
UserGroupId = groupId;
EntityId = entityId;
AssignedPermissions = assignedPermissions;
IsDefaultPermissions = false;
}
public EntityPermission(int groupId, int entityId, string[] assignedPermissions, bool isDefaultPermissions)
{
UserGroupId = groupId;
EntityId = entityId;
AssignedPermissions = assignedPermissions;
IsDefaultPermissions = isDefaultPermissions;
}
public int UserId { get; private set; }
public int EntityId { get; private set; }
public int UserGroupId { get; private set; }
/// <summary>
/// The assigned permissions for the user/entity combo
/// </summary>
public string[] AssignedPermissions { get; private set; }
/// <summary>
/// True if the permissions assigned to this object are the group's default permissions and not explicitly defined permissions
/// </summary>
/// <remarks>
/// This will be the case when looking up entity permissions and falling back to the default permissions
/// </remarks>
public bool IsDefaultPermissions { get; private set; }
public bool Equals(EntityPermission other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return EntityId == other.EntityId && UserGroupId == other.UserGroupId;
}
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((EntityPermission) obj);
}
public override int GetHashCode()
{
unchecked
{
return (EntityId * 397) ^ UserGroupId;
}
}
}
}
@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// A <see cref="HashSet{T}"/> of <see cref="EntityPermission"/>
/// </summary>
public class EntityPermissionCollection : HashSet<EntityPermission>
{
public EntityPermissionCollection()
{
}
public EntityPermissionCollection(IEnumerable<EntityPermission> collection) : base(collection)
{
}
/// <summary>
/// Returns the aggregate permissions in the permission set
/// </summary>
/// <returns></returns>
/// <remarks>
/// This value is only calculated once
/// </remarks>
public IEnumerable<string> GetAllPermissions()
{
return _aggregatePermissions ?? (_aggregatePermissions =
this.SelectMany(x => x.AssignedPermissions).Distinct().ToArray());
}
private string[] _aggregatePermissions;
}
}
@@ -1,59 +1,55 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents an entity -> user & permission key value pair collection
/// </summary>
/// Represents an entity -> user group & permission key value pair collection
/// </summary>
public class EntityPermissionSet
{
private static readonly Lazy<EntityPermissionSet> EmptyInstance = new Lazy<EntityPermissionSet>(() => new EntityPermissionSet(-1, new EntityPermissionCollection()));
/// <summary>
/// Returns an empty permission set
/// </summary>
/// <returns></returns>
public static EntityPermissionSet Empty()
{
return EmptyInstance.Value;
}
public EntityPermissionSet(int entityId, EntityPermissionCollection permissionsSet)
{
EntityId = entityId;
PermissionsSet = permissionsSet;
}
/// <summary>
/// The entity id with permissions assigned
/// </summary>
public int EntityId { get; private set; }
public virtual int EntityId { get; private set; }
/// <summary>
/// The key/value pairs of user id & single permission
/// The key/value pairs of user group id & single permission
/// </summary>
public IEnumerable<UserPermission> UserPermissionsSet { get; private set; }
public EntityPermissionCollection PermissionsSet { get; private set; }
public EntityPermissionSet(int entityId, IEnumerable<UserPermission> userPermissionsSet)
/// <summary>
/// Returns the aggregate permissions in the permission set
/// </summary>
/// <returns></returns>
/// <remarks>
/// This value is only calculated once
/// </remarks>
public IEnumerable<string> GetAllPermissions()
{
EntityId = entityId;
UserPermissionsSet = userPermissionsSet;
return PermissionsSet.GetAllPermissions();
}
public class UserPermission
{
public UserPermission(int userId, string permission)
{
UserId = userId;
Permission = permission;
}
public int UserId { get; private set; }
public string Permission { get; private set; }
protected bool Equals(UserPermission other)
{
return UserId == other.UserId && string.Equals(Permission, other.Permission);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UserPermission) obj);
}
public override int GetHashCode()
{
unchecked
{
return (UserId*397) ^ Permission.GetHashCode();
}
}
}
}
}
@@ -1,15 +1,11 @@
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Defines the the Profile interface
/// </summary>
/// <remarks>
/// This interface is pretty useless but has been exposed publicly from 6.x so we're stuck with it. It would make more sense
/// if the Id was an int but since it's not people have to cast it to int all of the time!
/// </remarks>
/// Defines the the User Profile interface
/// </summary>
public interface IProfile
{
object Id { get; set; }
string Name { get; set; }
int Id { get; }
string Name { get; }
}
}
@@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// A readonly user group providing basic information
/// </summary>
public interface IReadOnlyUserGroup
{
string Name { get; }
string Icon { get; }
int Id { get; }
int? StartContentId { get; }
int? StartMediaId { get; }
/// <summary>
/// The alias
/// </summary>
string Alias { get; }
/// <summary>
/// The set of default permissions
/// </summary>
/// <remarks>
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
IEnumerable<string> Permissions { get; set; }
IEnumerable<string> AllowedSections { get; }
}
}
+36 -20
View File
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models.Membership
{
@@ -10,29 +11,39 @@ namespace Umbraco.Core.Models.Membership
/// <remarks>Will be left internal until a proper Membership implementation is part of the roadmap</remarks>
public interface IUser : IMembershipUser, IRememberBeingDirty, ICanBeDirty
{
UserState UserState { get; }
string Name { get; set; }
int SessionTimeout { get; set; }
int StartContentId { get; set; }
int StartMediaId { get; set; }
string Language { get; set; }
int[] StartContentIds { get; set; }
int[] StartMediaIds { get; set; }
string Language { get; set; }
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUserType UserType { get; set; }
DateTime? EmailConfirmedDate { get; set; }
DateTime? InvitedDate { get; set; }
/// <summary>
/// Gets the groups that user is part of
/// </summary>
IEnumerable<IReadOnlyUserGroup> Groups { get; }
void RemoveGroup(string group);
void ClearGroups();
void AddGroup(IReadOnlyUserGroup group);
/// <summary>
/// Gets/sets the user type for the user
/// </summary>
IUserType UserType { get; set; }
//TODO: This should be a private set
/// <summary>
/// The default permission set for the user
/// </summary>
/// <remarks>
/// Currently in umbraco each permission is a single char but with an Enumerable{string} collection this allows for flexible changes to this in the future
/// </remarks>
IEnumerable<string> DefaultPermissions { get; set; }
IEnumerable<string> AllowedSections { get; }
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
void RemoveAllowedSection(string sectionAlias);
void AddAllowedSection(string sectionAlias);
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
void AddAllowedSection(string sectionAlias);
/// <summary>
/// Exposes the basic profile data
@@ -43,5 +54,10 @@ namespace Umbraco.Core.Models.Membership
/// The security stamp used by ASP.Net identity
/// </summary>
string SecurityStamp { get; set; }
/// <summary>
/// Will hold the media file system relative path of the users custom avatar if they uploaded one
/// </summary>
string Avatar { get; set; }
}
}
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models.Membership
{
public interface IUserGroup : IAggregateRoot
{
string Alias { get; set; }
int? StartContentId { get; set; }
int? StartMediaId { get; set; }
/// <summary>
/// The icon
/// </summary>
string Icon { get; set; }
/// <summary>
/// The name
/// </summary>
string Name { get; set; }
/// <summary>
/// The set of default permissions
/// </summary>
/// <remarks>
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
IEnumerable<string> Permissions { get; set; }
IEnumerable<string> AllowedSections { get; }
void RemoveAllowedSection(string sectionAlias);
void AddAllowedSection(string sectionAlias);
void ClearAllowedSections();
/// <summary>
/// Specifies the number of users assigned to this group
/// </summary>
int UserCount { get; }
}
}
+16 -27
View File
@@ -1,28 +1,17 @@
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models.Membership
{
public interface IUserType : IAggregateRoot
{
/// <summary>
/// The user type alias
/// </summary>
string Alias { get; set; }
/// <summary>
/// The user type name
/// </summary>
string Name { get; set; }
/// <summary>
/// The set of default permissions for the user type
/// </summary>
/// <remarks>
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
IEnumerable<string> Permissions { get; set; }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models.Membership
{
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IUserType : IAggregateRoot
{
string Alias { get; set; }
string Name { get; set; }
IEnumerable<string> Permissions { get; set; }
}
}
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Models.Membership
{
public class ReadOnlyUserGroup : IReadOnlyUserGroup, IEquatable<ReadOnlyUserGroup>
{
public ReadOnlyUserGroup(int id, string name, string icon, int? startContentId, int? startMediaId, string @alias,
IEnumerable<string> allowedSections, IEnumerable<string> permissions)
{
Name = name;
Icon = icon;
Id = id;
Alias = alias;
AllowedSections = allowedSections.ToArray();
Permissions = permissions.ToArray();
//Zero is invalid and will be treated as Null
StartContentId = startContentId == 0 ? null : startContentId;
StartMediaId = startMediaId == 0 ? null : startMediaId;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Icon { get; private set; }
public int? StartContentId { get; private set; }
public int? StartMediaId { get; private set; }
public string Alias { get; private set; }
/// <summary>
/// The set of default permissions
/// </summary>
/// <remarks>
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
public IEnumerable<string> Permissions { get; set; }
public IEnumerable<string> AllowedSections { get; private set; }
public bool Equals(ReadOnlyUserGroup other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Alias, other.Alias);
}
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((ReadOnlyUserGroup) obj);
}
public override int GetHashCode()
{
return Alias.GetHashCode();
}
public static bool operator ==(ReadOnlyUserGroup left, ReadOnlyUserGroup right)
{
return Equals(left, right);
}
public static bool operator !=(ReadOnlyUserGroup left, ReadOnlyUserGroup right)
{
return !Equals(left, right);
}
}
}
@@ -34,12 +34,11 @@ namespace Umbraco.Core.Models.Membership
/// <param name="lastLockoutDate">The last lockout date.</param>
/// <param name="fullName">The full name.</param>
/// <param name="language">The language.</param>
/// <param name="userType">Type of the user.</param>
/// <param name="user"></param>
public UmbracoMembershipUser(string providerName, string name, object providerUserKey, string email,
string passwordQuestion, string comment, bool isApproved, bool isLockedOut,
DateTime creationDate, DateTime lastLoginDate, DateTime lastActivityDate, DateTime lastPasswordChangedDate,
DateTime lastLockoutDate, string fullName, string language, IUserType userType, T user)
DateTime lastLockoutDate, string fullName, string language, T user)
: base( providerName, name, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOut,
creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockoutDate)
{
+359 -124
View File
@@ -2,13 +2,14 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
@@ -18,64 +19,107 @@ namespace Umbraco.Core.Models.Membership
[DataContract(IsReference = true)]
public class User : Entity, IUser
{
public User(IUserType userType)
/// <summary>
/// Constructor for creating a new/empty user
/// </summary>
public User()
{
if (userType == null) throw new ArgumentNullException("userType");
_userType = userType;
_defaultPermissions = _userType.Permissions == null ? Enumerable.Empty<string>() : new List<string>(_userType.Permissions);
//Groups = new List<object> { userType };
SessionTimeout = 60;
_sectionCollection = new ObservableCollection<string>();
_addedSections = new List<string>();
_removedSections = new List<string>();
_userGroups = new HashSet<IReadOnlyUserGroup>();
_language = GlobalSettings.DefaultUILanguage;
_sectionCollection.CollectionChanged += SectionCollectionChanged;
_isApproved = true;
_isLockedOut = false;
_startContentId = -1;
_startMediaId = -1;
_startContentIds = new int[] { };
_startMediaIds = new int[] { };
//cannot be null
_rawPasswordValue = "";
}
public User(string name, string email, string username, string rawPasswordValue, IUserType userType)
: this(userType)
/// <summary>
/// Constructor for creating a new/empty user
/// </summary>
/// <param name="name"></param>
/// <param name="email"></param>
/// <param name="username"></param>
/// <param name="rawPasswordValue"></param>
public User(string name, string email, string username, string rawPasswordValue)
: this()
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
if (string.IsNullOrWhiteSpace(email)) throw new ArgumentException("Value cannot be null or whitespace.", "email");
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", "username");
if (string.IsNullOrEmpty(rawPasswordValue)) throw new ArgumentException("Value cannot be null or empty.", "rawPasswordValue");
_name = name;
_email = email;
_username = username;
_rawPasswordValue = rawPasswordValue;
_userGroups = new HashSet<IReadOnlyUserGroup>();
_isApproved = true;
_isLockedOut = false;
_startContentId = -1;
_startMediaId = -1;
_startContentIds = new int[] { };
_startMediaIds = new int[] { };
}
/// <summary>
/// Constructor for creating a new User instance for an existing user
/// </summary>
/// <param name="id"></param>
/// <param name="name"></param>
/// <param name="email"></param>
/// <param name="username"></param>
/// <param name="rawPasswordValue"></param>
/// <param name="userGroups"></param>
/// <param name="startContentIds"></param>
/// <param name="startMediaIds"></param>
public User(int id, string name, string email, string username, string rawPasswordValue, IEnumerable<IReadOnlyUserGroup> userGroups, int[] startContentIds, int[] startMediaIds)
: this()
{
//we allow whitespace for this value so just check null
if (rawPasswordValue == null) throw new ArgumentNullException("rawPasswordValue");
if (userGroups == null) throw new ArgumentNullException("userGroups");
if (startContentIds == null) throw new ArgumentNullException("startContentIds");
if (startMediaIds == null) throw new ArgumentNullException("startMediaIds");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", "username");
Id = id;
_name = name;
_email = email;
_username = username;
_rawPasswordValue = rawPasswordValue;
_userGroups = new HashSet<IReadOnlyUserGroup>(userGroups);
_isApproved = true;
_isLockedOut = false;
_startContentIds = startContentIds;
_startMediaIds = startMediaIds;
}
private IUserType _userType;
private string _name;
private string _securityStamp;
private List<string> _addedSections;
private List<string> _removedSections;
private ObservableCollection<string> _sectionCollection;
private string _avatar;
private int _sessionTimeout;
private int _startContentId;
private int _startMediaId;
private int[] _startContentIds;
private int[] _startMediaIds;
private int _failedLoginAttempts;
private string _username;
private DateTime? _emailConfirmedDate;
private DateTime? _invitedDate;
private string _email;
private string _rawPasswordValue;
private IEnumerable<string> _allowedSections;
private HashSet<IReadOnlyUserGroup> _userGroups;
private bool _isApproved;
private bool _isLockedOut;
private string _language;
private DateTime _lastPasswordChangedDate;
private DateTime _lastLoginDate;
private DateTime _lastLockoutDate;
private IEnumerable<string> _defaultPermissions;
private bool _defaultToLiveEditing;
private IDictionary<string, object> _additionalData;
private object _additionalDataLock = new object();
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
@@ -87,10 +131,10 @@ namespace Umbraco.Core.Models.Membership
public readonly PropertyInfo LastPasswordChangeDateSelector = ExpressionHelper.GetPropertyInfo<User, DateTime>(x => x.LastPasswordChangeDate);
public readonly PropertyInfo SecurityStampSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.SecurityStamp);
public readonly PropertyInfo AvatarSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Avatar);
public readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.SessionTimeout);
public readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.StartContentId);
public readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.StartMediaId);
public readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.AllowedSections);
public readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<User, int[]>(x => x.StartContentIds);
public readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<User, int[]>(x => x.StartMediaIds);
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Name);
public readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Username);
@@ -99,9 +143,18 @@ namespace Umbraco.Core.Models.Membership
public readonly PropertyInfo IsLockedOutSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsLockedOut);
public readonly PropertyInfo IsApprovedSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsApproved);
public readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Language);
public readonly PropertyInfo EmailConfirmedDateSelector = ExpressionHelper.GetPropertyInfo<User, DateTime?>(x => x.EmailConfirmedDate);
public readonly PropertyInfo InvitedDateSelector = ExpressionHelper.GetPropertyInfo<User, DateTime?>(x => x.InvitedDate);
public readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.DefaultToLiveEditing);
public readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo<User, IUserType>(x => x.UserType);
public readonly PropertyInfo UserGroupsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<IReadOnlyUserGroup>>(x => x.Groups);
//Custom comparer for enumerable
public readonly DelegateEqualityComparer<IEnumerable<int>> IntegerEnumerableComparer =
new DelegateEqualityComparer<IEnumerable<int>>(
(enum1, enum2) => enum1.UnsortedSequenceEqual(enum2),
enum1 => enum1.GetHashCode());
}
#region Implementation of IMembershipUser
@@ -112,8 +165,19 @@ namespace Umbraco.Core.Models.Membership
get { return Id; }
set { throw new NotSupportedException("Cannot set the provider user key for a user"); }
}
[DataMember]
public DateTime? EmailConfirmedDate
{
get { return _emailConfirmedDate; }
set { SetPropertyValueAndDetectChanges(value, ref _emailConfirmedDate, Ps.Value.EmailConfirmedDateSelector); }
}
[DataMember]
public DateTime? InvitedDate
{
get { return _invitedDate; }
set { SetPropertyValueAndDetectChanges(value, ref _invitedDate, Ps.Value.InvitedDateSelector); }
}
[DataMember]
public string Username
{
@@ -189,6 +253,22 @@ namespace Umbraco.Core.Models.Membership
#region Implementation of IUser
public UserState UserState
{
get
{
if (LastLoginDate == default(DateTime) && IsApproved == false && InvitedDate != null)
return UserState.Invited;
if (IsLockedOut)
return UserState.LockedOut;
if (IsApproved == false)
return UserState.Disabled;
return UserState.Active;
}
}
[DataMember]
public string Name
{
@@ -198,28 +278,171 @@ namespace Umbraco.Core.Models.Membership
public IEnumerable<string> AllowedSections
{
get { return _sectionCollection; }
get { return _allowedSections ?? (_allowedSections = new List<string>(_userGroups.SelectMany(x => x.AllowedSections).Distinct())); }
}
public void RemoveAllowedSection(string sectionAlias)
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
IUserType IUser.UserType
{
if (_sectionCollection.Contains(sectionAlias))
get
{
_sectionCollection.Remove(sectionAlias);
//the best we can do here is to return the user's first user group as a IUserType object
//but we should attempt to return any group that is the built in ones first
var groups = Groups.ToArray();
if (groups.Length == 0)
{
//In backwards compatibility land, a user type cannot be null! so we need to return a fake one.
return new UserType
{
Alias = "temp",
Id = int.MinValue,
Key = Guid.Empty,
CreateDate = default(DateTime),
DeletedDate = null,
Name = "Temp",
Permissions = new List<string>(),
UpdateDate = default(DateTime)
};
}
var builtIns = new[] { Constants.Security.AdminGroupAlias, "writer", "editor", "translator" };
var foundBuiltIn = groups.FirstOrDefault(x => builtIns.Contains(x.Alias));
IUserGroup realGroup;
if (foundBuiltIn != null)
{
//if the group isn't IUserGroup we'll need to look it up
realGroup = foundBuiltIn as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(foundBuiltIn.Id);
//return a mapped version of the group
return new UserType
{
Alias = realGroup.Alias,
Id = realGroup.Id,
Key = realGroup.Key,
CreateDate = realGroup.CreateDate,
DeletedDate = realGroup.DeletedDate,
Name = realGroup.Name,
Permissions = realGroup.Permissions,
UpdateDate = realGroup.UpdateDate
};
}
//otherwise return the first
//if the group isn't IUserGroup we'll need to look it up
realGroup = groups[0] as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(groups[0].Id);
//return a mapped version of the group
return new UserType
{
Alias = realGroup.Alias,
Id = realGroup.Id,
Key = realGroup.Key,
CreateDate = realGroup.CreateDate,
DeletedDate = realGroup.DeletedDate,
Name = realGroup.Name,
Permissions = realGroup.Permissions,
UpdateDate = realGroup.UpdateDate
};
}
set
{
//if old APIs are still using this lets first check if the user is part of the user group with the alias specified
if (Groups.Any(x => x.Alias == value.Alias))
return;
//the only other option we have here is to lookup the group (and we'll need to use singletons here :( )
var found = ApplicationContext.Current.Services.UserService.GetUserGroupByAlias(value.Alias);
if (found == null)
throw new InvalidOperationException("No user group was found with the alias " + value.Alias + ", this API (IUser.UserType) is obsolete, use user groups instead");
//if it's found, all we can do is add it, we can't really replace them
AddGroup(found.ToReadOnlyGroup());
}
}
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
void IUser.RemoveAllowedSection(string sectionAlias)
{
//don't do anything if they aren't allowed it already
if (AllowedSections.Contains(sectionAlias) == false)
return;
var groups = Groups.ToArray();
//our only option here is to check if a custom group is created for this user, if so we can remove it from that group, otherwise we'll throw
//now we'll check if the user has a special 1:1 user group created for itself. This will occur if this method is used and also during an upgrade.
//this comes in the alias form of userName + 'Group'
var customUserGroup = groups.FirstOrDefault(x => x.Alias == (Username + "Group"));
if (customUserGroup != null)
{
//if the group isn't IUserGroup we'll need to look it up
var realGroup = customUserGroup as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(customUserGroup.Id);
realGroup.RemoveAllowedSection(sectionAlias);
//now we need to flag this for saving (hack!)
GroupsToSave.Add(realGroup);
}
else
{
throw new InvalidOperationException("Cannot remove the allowed section using this obsolete API. Modify the user's groups instead");
}
}
public void AddAllowedSection(string sectionAlias)
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
void IUser.AddAllowedSection(string sectionAlias)
{
if (_sectionCollection.Contains(sectionAlias) == false)
//don't do anything if they are allowed it already
if (AllowedSections.Contains(sectionAlias))
return;
//This is here for backwards compat only.
//First we'll check if the user is part of the 'admin' group. If so then we can ensure that the admin group has this section available to it.
//otherwise, the only thing we can do is create a custom user group for this user and add this section.
//We are checking for admin here because if the user is an admin and an allowed section is being added, then it's assumed it's to be added
//for the whole admin group (i.e. Forms installer does this for admins)
var groups = Groups.ToArray();
var admin = groups.FirstOrDefault(x => x.Alias == Constants.Security.AdminGroupAlias);
if (admin != null)
{
//if the group isn't IUserGroup we'll need to look it up
var realGroup = admin as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(admin.Id);
realGroup.AddAllowedSection(sectionAlias);
//now we need to flag this for saving (hack!)
GroupsToSave.Add(realGroup);
}
//now we'll check if the user has a special 1:1 user group created for itself. This will occur if this method is used and also during an upgrade.
//this comes in the alias form of userName + 'Group'
var customUserGroup = groups.FirstOrDefault(x => x.Alias == (Username + "Group"));
if (customUserGroup != null)
{
_sectionCollection.Add(sectionAlias);
}
}
//if the group isn't IUserGroup we'll need to look it up
var realGroup = customUserGroup as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(customUserGroup.Id);
realGroup.AddAllowedSection(sectionAlias);
//now we need to flag this for saving (hack!)
GroupsToSave.Add(realGroup);
}
//ok, so the user doesn't have a 1:1 group, we'll need to flag it for creation
var newUserGroup = new UserGroup
{
Alias = Username + "Group",
Name = "Group for " + Username
};
newUserGroup.AddAllowedSection(sectionAlias);
GroupsToSave.Add(newUserGroup);
}
/// <summary>
/// This used purely for hacking backwards compatibility into this class for &lt; 7.7 compat
/// </summary>
[DoNotClone]
[IgnoreDataMember]
internal List<IUserGroup> GroupsToSave = new List<IUserGroup>();
public IProfile ProfileData
{
get { return new UserProfile(this); }
get { return new WrappedUserProfile(this); }
}
/// <summary>
@@ -232,21 +455,12 @@ namespace Umbraco.Core.Models.Membership
set { SetPropertyValueAndDetectChanges(value, ref _securityStamp, Ps.Value.SecurityStampSelector); }
}
/// <summary>
/// Used internally to check if we need to add a section in the repository to the db
/// </summary>
internal IEnumerable<string> AddedSections
[DataMember]
public string Avatar
{
get { return _addedSections; }
}
/// <summary>
/// Used internally to check if we need to remove a section in the repository to the db
/// </summary>
internal IEnumerable<string> RemovedSections
{
get { return _removedSections; }
}
get { return _avatar; }
set { SetPropertyValueAndDetectChanges(value, ref _avatar, Ps.Value.AvatarSelector); }
}
/// <summary>
/// Gets or sets the session timeout.
@@ -268,10 +482,11 @@ namespace Umbraco.Core.Models.Membership
/// The start content id.
/// </value>
[DataMember]
public int StartContentId
[DoNotClone]
public int[] StartContentIds
{
get { return _startContentId; }
set { SetPropertyValueAndDetectChanges(value, ref _startContentId, Ps.Value.StartContentIdSelector); }
get { return _startContentIds; }
set { SetPropertyValueAndDetectChanges(value, ref _startContentIds, Ps.Value.StartContentIdSelector, Ps.Value.IntegerEnumerableComparer); }
}
/// <summary>
@@ -281,10 +496,11 @@ namespace Umbraco.Core.Models.Membership
/// The start media id.
/// </value>
[DataMember]
public int StartMediaId
[DoNotClone]
public int[] StartMediaIds
{
get { return _startMediaId; }
set { SetPropertyValueAndDetectChanges(value, ref _startMediaId, Ps.Value.StartMediaIdSelector); }
get { return _startMediaIds; }
set { SetPropertyValueAndDetectChanges(value, ref _startMediaIds, Ps.Value.StartMediaIdSelector, Ps.Value.IntegerEnumerableComparer); }
}
[DataMember]
@@ -294,14 +510,6 @@ namespace Umbraco.Core.Models.Membership
set { SetPropertyValueAndDetectChanges(value, ref _language, Ps.Value.LanguageSelector); }
}
//TODO: This should be a private set
[DataMember]
public IEnumerable<string> DefaultPermissions
{
get { return _defaultPermissions;}
set { _defaultPermissions = value; }
}
[IgnoreDataMember]
internal bool DefaultToLiveEditing
{
@@ -309,78 +517,107 @@ namespace Umbraco.Core.Models.Membership
set { SetPropertyValueAndDetectChanges(value, ref _defaultToLiveEditing, Ps.Value.DefaultToLiveEditingSelector); }
}
[IgnoreDataMember]
public IUserType UserType
/// <summary>
/// Gets the groups that user is part of
/// </summary>
[DataMember]
public IEnumerable<IReadOnlyUserGroup> Groups
{
get { return _userType; }
set
get { return _userGroups; }
}
public void RemoveGroup(string group)
{
foreach (var userGroup in _userGroups.ToArray())
{
if (value.HasIdentity == false)
if (userGroup.Alias == group)
{
throw new InvalidOperationException("Cannot assign a User Type that has not been persisted");
_userGroups.Remove(userGroup);
//reset this flag so it's rebuilt with the assigned groups
_allowedSections = null;
OnPropertyChanged(Ps.Value.UserGroupsSelector);
}
SetPropertyValueAndDetectChanges(value, ref _userType, Ps.Value.UserTypeSelector);
}
}
public void ClearGroups()
{
if (_userGroups.Count > 0)
{
_userGroups.Clear();
//reset this flag so it's rebuilt with the assigned groups
_allowedSections = null;
OnPropertyChanged(Ps.Value.UserGroupsSelector);
}
}
public void AddGroup(IReadOnlyUserGroup group)
{
if (_userGroups.Add(group))
{
//reset this flag so it's rebuilt with the assigned groups
_allowedSections = null;
OnPropertyChanged(Ps.Value.UserGroupsSelector);
}
}
#endregion
/// <summary>
/// Whenever resetting occurs, clear the remembered add/removed collections, even if
/// rememberPreviouslyChangedProperties is true, the AllowedSections property will still
/// be flagged as dirty.
/// This is used as an internal cache for this entity - specifically for calculating start nodes so we don't re-calculated all of the time
/// </summary>
/// <param name="rememberPreviouslyChangedProperties"></param>
public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
[IgnoreDataMember]
[DoNotClone]
internal IDictionary<string, object> AdditionalData
{
_addedSections.Clear();
_removedSections.Clear();
base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
}
/// <summary>
/// Handles the collection changed event in order for us to flag the AllowedSections property as changed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(Ps.Value.AllowedSectionsSelector);
if (e.Action == NotifyCollectionChangedAction.Add)
get
{
var item = e.NewItems.Cast<string>().First();
if (_addedSections.Contains(item) == false)
lock (_additionalDataLock)
{
_addedSections.Add(item);
return _additionalData ?? (_additionalData = new Dictionary<string, object>());
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
var item = e.OldItems.Cast<string>().First();
if (_removedSections.Contains(item) == false)
{
_removedSections.Add(item);
}
}
}
[IgnoreDataMember]
[DoNotClone]
internal object AdditionalDataLock { get { return _additionalDataLock; } }
public override object DeepClone()
{
var clone = (User)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//manually clone the start node props
clone._startContentIds = _startContentIds.ToArray();
clone._startMediaIds = _startMediaIds.ToArray();
// this value has been cloned and points to the same object
// which obviously is bad - needs to point to a new object
clone._additionalDataLock = new object();
if (_additionalData != null)
{
// clone._additionalData points to the same dictionary, which is bad, because
// changing one clone impacts all of them - so we need to reset it with a fresh
// dictionary that will contain the same values - and, if some values are deep
// cloneable, they should be deep-cloned too
var cloneAdditionalData = clone._additionalData = new Dictionary<string, object>();
lock (_additionalDataLock)
{
foreach (var kvp in _additionalData)
{
var deepCloneable = kvp.Value as IDeepCloneable;
cloneAdditionalData[kvp.Key] = deepCloneable == null ? kvp.Value : deepCloneable.DeepClone();
}
}
}
//need to create new collections otherwise they'll get copied by ref
clone._addedSections = new List<string>();
clone._removedSections = new List<string>();
clone._sectionCollection = new ObservableCollection<string>(_sectionCollection.ToList());
clone._defaultPermissions = new List<string>(_defaultPermissions.ToList());
clone._userGroups = new HashSet<IReadOnlyUserGroup>(_userGroups);
clone._allowedSections = _allowedSections != null ? new List<string>(_allowedSections) : null;
//re-create the event handler
clone._sectionCollection.CollectionChanged += clone.SectionCollectionChanged;
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
@@ -392,28 +629,26 @@ namespace Umbraco.Core.Models.Membership
/// <summary>
/// Internal class used to wrap the user in a profile
/// </summary>
private class UserProfile : IProfile
private class WrappedUserProfile : IProfile
{
private readonly IUser _user;
public UserProfile(IUser user)
public WrappedUserProfile(IUser user)
{
_user = user;
}
public object Id
public int Id
{
get { return _user.Id; }
set { _user.Id = (int)value; }
}
public string Name
{
get { return _user.Name; }
set { _user.Name = value; }
}
protected bool Equals(UserProfile other)
private bool Equals(WrappedUserProfile other)
{
return _user.Equals(other._user);
}
@@ -423,7 +658,7 @@ namespace Umbraco.Core.Models.Membership
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UserProfile) obj);
return Equals((WrappedUserProfile) obj);
}
public override int GetHashCode()
+141 -6
View File
@@ -1,20 +1,155 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents a Group for a Backoffice User
/// </summary>
/// <remarks>
/// Should be internal until a proper user/membership implementation
/// is part of the roadmap.
/// </remarks>
[Serializable]
[DataContract(IsReference = true)]
internal class UserGroup : Entity
internal class UserGroup : Entity, IUserGroup, IReadOnlyUserGroup
{
//Add UserCollection ?
private int? _startContentId;
private int? _startMediaId;
private string _alias;
private string _icon;
private string _name;
private IEnumerable<string> _permissions;
private readonly List<string> _sectionCollection;
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<UserGroup, string>(x => x.Name);
public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<UserGroup, string>(x => x.Alias);
public readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo<UserGroup, IEnumerable<string>>(x => x.Permissions);
public readonly PropertyInfo IconSelector = ExpressionHelper.GetPropertyInfo<UserGroup, string>(x => x.Icon);
public readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<UserGroup, int?>(x => x.StartContentId);
public readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<UserGroup, int?>(x => x.StartMediaId);
//Custom comparer for enumerable
public readonly DelegateEqualityComparer<IEnumerable<string>> StringEnumerableComparer =
new DelegateEqualityComparer<IEnumerable<string>>(
(enum1, enum2) => enum1.UnsortedSequenceEqual(enum2),
enum1 => enum1.GetHashCode());
}
/// <summary>
/// Constructor to create a new user group
/// </summary>
public UserGroup()
{
_sectionCollection = new List<string>();
}
/// <summary>
/// Constructor to create an existing user group
/// </summary>
/// <param name="userCount"></param>
/// <param name="alias"></param>
/// <param name="name"></param>
/// <param name="permissions"></param>
/// <param name="icon"></param>
public UserGroup(int userCount, string alias, string name, IEnumerable<string> permissions, string icon)
: this()
{
UserCount = userCount;
_alias = alias;
_name = name;
_permissions = permissions;
_icon = icon;
}
[DataMember]
public int? StartMediaId
{
get { return _startMediaId; }
set { SetPropertyValueAndDetectChanges(value, ref _startMediaId, Ps.Value.StartMediaIdSelector); }
}
[DataMember]
public int? StartContentId
{
get { return _startContentId; }
set { SetPropertyValueAndDetectChanges(value, ref _startContentId, Ps.Value.StartContentIdSelector); }
}
[DataMember]
public string Icon
{
get { return _icon; }
set { SetPropertyValueAndDetectChanges(value, ref _icon, Ps.Value.IconSelector); }
}
[DataMember]
public string Alias
{
get { return _alias; }
set
{
SetPropertyValueAndDetectChanges(
value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase),
ref _alias,
Ps.Value.AliasSelector);
}
}
[DataMember]
public string Name
{
get { return _name; }
set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
}
/// <summary>
/// The set of default permissions for the user group
/// </summary>
/// <remarks>
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
[DataMember]
public IEnumerable<string> Permissions
{
get { return _permissions; }
set
{
SetPropertyValueAndDetectChanges(value, ref _permissions, Ps.Value.PermissionsSelector,
Ps.Value.StringEnumerableComparer);
}
}
public IEnumerable<string> AllowedSections
{
get { return _sectionCollection; }
}
public void RemoveAllowedSection(string sectionAlias)
{
if (_sectionCollection.Contains(sectionAlias))
{
_sectionCollection.Remove(sectionAlias);
}
}
public void AddAllowedSection(string sectionAlias)
{
if (_sectionCollection.Contains(sectionAlias) == false)
{
_sectionCollection.Add(sectionAlias);
}
}
public void ClearAllowedSections()
{
_sectionCollection.Clear();
}
public int UserCount { get; private set; }
}
}
@@ -0,0 +1,26 @@
using Umbraco.Core.Models.Rdbms;
using System.Linq;
namespace Umbraco.Core.Models.Membership
{
internal static class UserGroupExtensions
{
public static IReadOnlyUserGroup ToReadOnlyGroup(this IUserGroup group)
{
//this will generally always be the case
var readonlyGroup = group as IReadOnlyUserGroup;
if (readonlyGroup != null) return readonlyGroup;
//otherwise create one
return new ReadOnlyUserGroup(group.Id, group.Name, group.Icon, group.StartContentId, group.StartMediaId, group.Alias, group.AllowedSections, group.Permissions);
}
public static IReadOnlyUserGroup ToReadOnlyGroup(this UserGroupDto group)
{
return new ReadOnlyUserGroup(group.Id, group.Name, group.Icon,
group.StartContentId, group.StartMediaId, group.Alias,
group.UserGroup2AppDtos.Select(x => x.AppAlias).ToArray(),
group.DefaultPermissions == null ? Enumerable.Empty<string>() : group.DefaultPermissions.ToCharArray().Select(x => x.ToString()));
}
}
}
@@ -0,0 +1,46 @@
using System;
namespace Umbraco.Core.Models.Membership
{
internal class UserProfile : IProfile, IEquatable<UserProfile>
{
public UserProfile(int id, string name)
{
Id = id;
Name = name;
}
public int Id { get; private set; }
public string Name { get; private set; }
public bool Equals(UserProfile other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UserProfile) obj);
}
public override int GetHashCode()
{
return Id;
}
public static bool operator ==(UserProfile left, UserProfile right)
{
return Equals(left, right);
}
public static bool operator !=(UserProfile left, UserProfile right)
{
return Equals(left, right) == false;
}
}
}
@@ -0,0 +1,14 @@
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// The state of a user
/// </summary>
public enum UserState
{
All = -1,
Active = 0,
Disabled = 1,
LockedOut = 2,
Invited = 3
}
}
+67 -71
View File
@@ -1,72 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents the Type for a Backoffice User
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
internal class UserType : Entity, IUserType
{
private string _alias;
private string _name;
private IEnumerable<string> _permissions;
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Name);
public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Alias);
public readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo<UserType, IEnumerable<string>>(x => x.Permissions);
}
[DataMember]
public string Alias
{
get { return _alias; }
set
{
SetPropertyValueAndDetectChanges(
value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase),
ref _alias,
Ps.Value.AliasSelector);
}
}
[DataMember]
public string Name
{
get { return _name; }
set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
}
/// <summary>
/// The set of default permissions for the user type
/// </summary>
/// <remarks>
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
[DataMember]
public IEnumerable<string> Permissions
{
get { return _permissions; }
set
{
SetPropertyValueAndDetectChanges(value, ref _permissions, Ps.Value.PermissionsSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<string>>(
(enum1, enum2) => enum1.UnsortedSequenceEqual(enum2),
enum1 => enum1.GetHashCode()));
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Models.Membership
{
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
[Serializable]
[DataContract(IsReference = true)]
internal class UserType : Entity, IUserType
{
private string _alias;
private string _name;
private IEnumerable<string> _permissions;
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
private class PropertySelectors
{
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Name);
public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Alias);
public readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo<UserType, IEnumerable<string>>(x => x.Permissions);
}
[DataMember]
public string Alias
{
get { return _alias; }
set
{
SetPropertyValueAndDetectChanges(
value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase),
ref _alias,
Ps.Value.AliasSelector);
}
}
[DataMember]
public string Name
{
get { return _name; }
set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); }
}
/// <summary>
/// The set of default permissions for the user type
/// </summary>
/// <remarks>
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
/// </remarks>
[DataMember]
public IEnumerable<string> Permissions
{
get { return _permissions; }
set
{
SetPropertyValueAndDetectChanges(value, ref _permissions, Ps.Value.PermissionsSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<string>>(
(enum1, enum2) => enum1.UnsortedSequenceEqual(enum2),
enum1 => enum1.GetHashCode()));
}
}
}
}
+25 -25
View File
@@ -49,36 +49,36 @@ namespace Umbraco.Core.Models
{
public readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo<Property, object>(x => x.Value);
public readonly PropertyInfo VersionSelector = ExpressionHelper.GetPropertyInfo<Property, Guid>(x => x.Version);
}
private static readonly DelegateEqualityComparer<object> ValueComparer = new DelegateEqualityComparer<object>(
(o, o1) =>
{
if (o == null && o1 == null) return true;
//custom comparer for strings.
if (o is string || o1 is string)
public readonly DelegateEqualityComparer<object> PropertyValueComparer = new DelegateEqualityComparer<object>(
(o, o1) =>
{
//if one is null and another is empty then they are the same
if ((o as string).IsNullOrWhiteSpace() && (o1 as string).IsNullOrWhiteSpace())
if (o == null && o1 == null) return true;
//custom comparer for strings.
if (o is string || o1 is string)
{
return true;
//if one is null and another is empty then they are the same
if ((o as string).IsNullOrWhiteSpace() && (o1 as string).IsNullOrWhiteSpace())
{
return true;
}
if (o == null || o1 == null) return false;
return o.Equals(o1);
}
if (o == null || o1 == null) return false;
//Custom comparer for enumerable if it is enumerable
var enum1 = o as IEnumerable;
var enum2 = o1 as IEnumerable;
if (enum1 != null && enum2 != null)
{
return enum1.Cast<object>().UnsortedSequenceEqual(enum2.Cast<object>());
}
return o.Equals(o1);
}
if (o == null || o1 == null) return false;
//Custom comparer for enumerable if it is enumerable
var enum1 = o as IEnumerable;
var enum2 = o1 as IEnumerable;
if (enum1 != null && enum2 != null)
{
return enum1.Cast<object>().UnsortedSequenceEqual(enum2.Cast<object>());
}
return o.Equals(o1);
}, o => o.GetHashCode());
}, o => o.GetHashCode());
}
/// <summary>
/// Returns the instance of the tag support, by default tags are not enabled
@@ -200,7 +200,7 @@ namespace Umbraco.Core.Models
}
}
SetPropertyValueAndDetectChanges(value, ref _value, Ps.Value.ValueSelector, ValueComparer);
SetPropertyValueAndDetectChanges(value, ref _value, Ps.Value.ValueSelector, Ps.Value.PropertyValueComparer);
}
}
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Models.PublishedContent
if (_index.HasValue) return _index.Value;
// slow -- and don't cache, not in a set
if (_contentSet == null) return Content.GetIndex();
if (_contentSet == null) return WrappedContentInternal.GetIndex();
// slow -- but cache for next time
var index = _contentSet.FindIndex(x => x.Id == Id);
@@ -147,7 +147,7 @@ namespace Umbraco.Core.Models.PublishedContent
public override IEnumerable<IPublishedContent> ContentSet
{
get { return _contentSet ?? Content.ContentSet; }
get { return _contentSet ?? WrappedContentInternal.ContentSet; }
}
#endregion
@@ -161,8 +161,8 @@ namespace Umbraco.Core.Models.PublishedContent
get
{
return _properties == null
? Content.Properties
: Content.Properties.Union(_properties).ToList();
? WrappedContentInternal.Properties
: WrappedContentInternal.Properties.Union(_properties).ToList();
}
}
@@ -175,15 +175,15 @@ namespace Umbraco.Core.Models.PublishedContent
var property = _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias));
if (property != null) return property.HasValue ? property.Value : null;
}
return Content[alias];
return WrappedContentInternal[alias];
}
}
public override IPublishedProperty GetProperty(string alias)
{
return _properties == null
? Content.GetProperty(alias)
: _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? Content.GetProperty(alias);
? WrappedContentInternal.GetProperty(alias)
: _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? WrappedContentInternal.GetProperty(alias);
}
#endregion
@@ -9,6 +9,6 @@ namespace Umbraco.Core.Models.PublishedContent
: base(content)
{ }
public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
public Guid Key { get { return ((IPublishedContentWithKey) WrappedContentInternal).Key; } }
}
}
@@ -8,6 +8,6 @@ namespace Umbraco.Core.Models.PublishedContent
: base (content)
{ }
public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
public Guid Key { get { return ((IPublishedContentWithKey) WrappedContentInternal).Key; } }
}
}
@@ -12,6 +12,6 @@ namespace Umbraco.Core.Models.PublishedContent
: base(content)
{ }
public virtual Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
public virtual Guid Key { get { return ((IPublishedContentWithKey) WrappedContentInternal).Key; } }
}
}
@@ -27,7 +27,7 @@ namespace Umbraco.Core.Models.PublishedContent
/// </summary>
public abstract class PublishedContentWrapped : IPublishedContent
{
protected readonly IPublishedContent Content;
protected readonly IPublishedContent WrappedContentInternal;
/// <summary>
/// Initialize a new instance of the <see cref="PublishedContentWrapped"/> class
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Models.PublishedContent
/// <param name="content">The content to wrap and extend.</param>
protected PublishedContentWrapped(IPublishedContent content)
{
Content = content;
WrappedContentInternal = content;
}
/// <summary>
@@ -45,21 +45,21 @@ namespace Umbraco.Core.Models.PublishedContent
/// <returns>The wrapped content, that was passed as an argument to the constructor.</returns>
public IPublishedContent Unwrap()
{
return Content;
return WrappedContentInternal;
}
#region ContentSet
public virtual IEnumerable<IPublishedContent> ContentSet
{
get { return Content.ContentSet; }
get { return WrappedContentInternal.ContentSet; }
}
#endregion
#region ContentType
public virtual PublishedContentType ContentType { get { return Content.ContentType; } }
public virtual PublishedContentType ContentType { get { return WrappedContentInternal.ContentType; } }
#endregion
@@ -67,102 +67,102 @@ namespace Umbraco.Core.Models.PublishedContent
public virtual int Id
{
get { return Content.Id; }
get { return WrappedContentInternal.Id; }
}
public virtual int TemplateId
{
get { return Content.TemplateId; }
get { return WrappedContentInternal.TemplateId; }
}
public virtual int SortOrder
{
get { return Content.SortOrder; }
get { return WrappedContentInternal.SortOrder; }
}
public virtual string Name
{
get { return Content.Name; }
get { return WrappedContentInternal.Name; }
}
public virtual string UrlName
{
get { return Content.UrlName; }
get { return WrappedContentInternal.UrlName; }
}
public virtual string DocumentTypeAlias
{
get { return Content.DocumentTypeAlias; }
get { return WrappedContentInternal.DocumentTypeAlias; }
}
public virtual int DocumentTypeId
{
get { return Content.DocumentTypeId; }
get { return WrappedContentInternal.DocumentTypeId; }
}
public virtual string WriterName
{
get { return Content.WriterName; }
get { return WrappedContentInternal.WriterName; }
}
public virtual string CreatorName
{
get { return Content.CreatorName; }
get { return WrappedContentInternal.CreatorName; }
}
public virtual int WriterId
{
get { return Content.WriterId; }
get { return WrappedContentInternal.WriterId; }
}
public virtual int CreatorId
{
get { return Content.CreatorId; }
get { return WrappedContentInternal.CreatorId; }
}
public virtual string Path
{
get { return Content.Path; }
get { return WrappedContentInternal.Path; }
}
public virtual DateTime CreateDate
{
get { return Content.CreateDate; }
get { return WrappedContentInternal.CreateDate; }
}
public virtual DateTime UpdateDate
{
get { return Content.UpdateDate; }
get { return WrappedContentInternal.UpdateDate; }
}
public virtual Guid Version
{
get { return Content.Version; }
get { return WrappedContentInternal.Version; }
}
public virtual int Level
{
get { return Content.Level; }
get { return WrappedContentInternal.Level; }
}
public virtual string Url
{
get { return Content.Url; }
get { return WrappedContentInternal.Url; }
}
public virtual PublishedItemType ItemType
{
get { return Content.ItemType; }
get { return WrappedContentInternal.ItemType; }
}
public virtual bool IsDraft
{
get { return Content.IsDraft; }
get { return WrappedContentInternal.IsDraft; }
}
public virtual int GetIndex()
{
return Content.GetIndex();
return WrappedContentInternal.GetIndex();
}
#endregion
@@ -171,12 +171,12 @@ namespace Umbraco.Core.Models.PublishedContent
public virtual IPublishedContent Parent
{
get { return Content.Parent; }
get { return WrappedContentInternal.Parent; }
}
public virtual IEnumerable<IPublishedContent> Children
{
get { return Content.Children; }
get { return WrappedContentInternal.Children; }
}
#endregion
@@ -185,22 +185,22 @@ namespace Umbraco.Core.Models.PublishedContent
public virtual ICollection<IPublishedProperty> Properties
{
get { return Content.Properties; }
get { return WrappedContentInternal.Properties; }
}
public virtual object this[string alias]
{
get { return Content[alias]; }
get { return WrappedContentInternal[alias]; }
}
public virtual IPublishedProperty GetProperty(string alias)
{
return Content.GetProperty(alias);
return WrappedContentInternal.GetProperty(alias);
}
public virtual IPublishedProperty GetProperty(string alias, bool recurse)
{
return Content.GetProperty(alias, recurse);
return WrappedContentInternal.GetProperty(alias, recurse);
}
#endregion
@@ -24,7 +24,8 @@ namespace Umbraco.Core.Models.Rdbms
public Guid? Parent { get; set; }
[Column("key")]
[Length(1000)]
[Length(450)]
[Index(IndexTypes.NonClustered, Name = "IX_cmsDictionary_key")]
public string Key { get; set; }
[ResultColumn]
@@ -0,0 +1,19 @@
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoUser2UserGroup")]
[ExplicitColumns]
internal class User2UserGroupDto
{
[Column("userId")]
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_user2userGroup", OnColumns = "userId, userGroupId")]
[ForeignKey(typeof(UserDto))]
public int UserId { get; set; }
[Column("userGroupId")]
[ForeignKey(typeof(UserGroupDto))]
public int UserGroupId { get; set; }
}
}
+46 -14
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -10,6 +11,12 @@ namespace Umbraco.Core.Models.Rdbms
[ExplicitColumns]
internal class UserDto
{
public UserDto()
{
UserGroupDtos = new List<UserGroupDto>();
UserStartNodeDtos = new HashSet<UserStartNodeDto>();
}
[Column("id")]
[PrimaryKeyColumn(Name = "PK_user")]
public int Id { get; set; }
@@ -21,18 +28,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("userNoConsole")]
[Constraint(Default = "0")]
public bool NoConsole { get; set; }
[Column("userType")]
[ForeignKey(typeof(UserTypeDto))]
public short Type { get; set; }
[Column("startStructureID")]
public int ContentStartId { get; set; }
[Column("startMediaID")]
[NullSetting(NullSetting = NullSettings.Null)]
public int? MediaStartId { get; set; }
[Column("userName")]
public string UserName { get; set; }
@@ -44,6 +40,14 @@ namespace Umbraco.Core.Models.Rdbms
[Column("userPassword")]
[Length(500)]
public string Password { get; set; }
/// <summary>
/// This will represent a JSON structure of how the password has been created (i.e hash algorithm, iterations)
/// </summary>
[Column("passwordConfig")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(500)]
public string PasswordConfig { get; set; }
[Column("userEmail")]
public string Email { get; set; }
@@ -73,8 +77,36 @@ namespace Umbraco.Core.Models.Rdbms
[Column("lastLoginDate")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? LastLoginDate { get; set; }
[Column("emailConfirmedDate")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? EmailConfirmedDate { get; set; }
[Column("invitedDate")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? InvitedDate { get; set; }
[Column("createDate")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime CreateDate { get; set; }
[Column("updateDate")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime UpdateDate { get; set; }
/// <summary>
/// Will hold the media file system relative path of the users custom avatar if they uploaded one
/// </summary>
[Column("avatar")]
[NullSetting(NullSetting = NullSettings.Null)]
public string Avatar { get; set; }
[ResultColumn]
public List<User2AppDto> User2AppDtos { get; set; }
public List<UserGroupDto> UserGroupDtos { get; set; }
[ResultColumn]
public HashSet<UserStartNodeDto> UserStartNodeDtos { get; set; }
}
}
@@ -1,20 +1,19 @@
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoUser2app")]
[PrimaryKey("user", autoIncrement = false)]
[ExplicitColumns]
internal class User2AppDto
{
[Column("user")]
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_user2app", OnColumns = "user, app")]
[ForeignKey(typeof(UserDto))]
public int UserId { get; set; }
[Column("app")]
[Length(50)]
public string AppAlias { get; set; }
}
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoUserGroup2App")]
[ExplicitColumns]
internal class UserGroup2AppDto
{
[Column("userGroupId")]
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_userGroup2App", OnColumns = "userGroupId, app")]
[ForeignKey(typeof(UserGroupDto))]
public int UserGroupId { get; set; }
[Column("app")]
[Length(50)]
public string AppAlias { get; set; }
}
}
@@ -3,15 +3,14 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoUser2NodePermission")]
[PrimaryKey("userId", autoIncrement = false)]
[TableName("umbracoUserGroup2NodePermission")]
[ExplicitColumns]
internal class User2NodePermissionDto
internal class UserGroup2NodePermissionDto
{
[Column("userId")]
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_umbracoUser2NodePermission", OnColumns = "userId, nodeId, permission")]
[ForeignKey(typeof(UserDto))]
public int UserId { get; set; }
[Column("userGroupId")]
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_umbracoUserGroup2NodePermission", OnColumns = "userGroupId, nodeId, permission")]
[ForeignKey(typeof(UserGroupDto))]
public int UserGroupId { get; set; }
[Column("nodeId")]
[ForeignKey(typeof(NodeDto))]
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoUserGroup")]
[PrimaryKey("id")]
[ExplicitColumns]
internal class UserGroupDto
{
public UserGroupDto()
{
UserGroup2AppDtos = new List<UserGroup2AppDto>();
}
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 5)]
public int Id { get; set; }
[Column("userGroupAlias")]
[Length(200)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoUserGroup_userGroupAlias")]
public string Alias { get; set; }
[Column("userGroupName")]
[Length(200)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoUserGroup_userGroupName")]
public string Name { get; set; }
[Column("userGroupDefaultPermissions")]
[Length(50)]
[NullSetting(NullSetting = NullSettings.Null)]
public string DefaultPermissions { get; set; }
[Column("createDate")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime CreateDate { get; set; }
[Column("updateDate")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime UpdateDate { get; set; }
[Column("icon")]
[NullSetting(NullSetting = NullSettings.Null)]
public string Icon { get; set; }
[Column("startContentId")]
[NullSetting(NullSetting = NullSettings.Null)]
public int? StartContentId { get; set; }
[Column("startMediaId")]
[NullSetting(NullSetting = NullSettings.Null)]
public int? StartMediaId { get; set; }
[ResultColumn]
public List<UserGroup2AppDto> UserGroup2AppDtos { get; set; }
/// <summary>
/// This is only relevant when this column is included in the results (i.e. GetUserGroupsWithUserCounts)
/// </summary>
[ResultColumn]
public int UserCount { get; set; }
}
}
@@ -0,0 +1,67 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoUserStartNode")]
[PrimaryKey("id", autoIncrement = true)]
[ExplicitColumns]
internal class UserStartNodeDto : IEquatable<UserStartNodeDto>
{
[Column("id")]
[PrimaryKeyColumn(Name = "PK_userStartNode")]
public int Id { get; set; }
[Column("userId")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[ForeignKey(typeof(UserDto))]
public int UserId { get; set; }
[Column("startNode")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[ForeignKey(typeof(NodeDto))]
public int StartNode { get; set; }
[Column("startNodeType")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Index(IndexTypes.UniqueNonClustered, ForColumns = "startNodeType, startNode, userId", Name = "IX_umbracoUserStartNode_startNodeType")]
public int StartNodeType { get; set; }
public enum StartNodeTypeValue
{
Content = 1,
Media = 2
}
public bool Equals(UserStartNodeDto other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UserStartNodeDto) obj);
}
public override int GetHashCode()
{
return Id;
}
public static bool operator ==(UserStartNodeDto left, UserStartNodeDto right)
{
return Equals(left, right);
}
public static bool operator !=(UserStartNodeDto left, UserStartNodeDto right)
{
return !Equals(left, right);
}
}
}
+3 -1
View File
@@ -1,8 +1,10 @@
using Umbraco.Core.Persistence;
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[Obsolete("Table no longer exists as of 7.6 - retained only to support migrations from previous versions")]
[TableName("umbracoUserType")]
[PrimaryKey("id")]
[ExplicitColumns]
@@ -185,6 +185,14 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Language)]
[FriendlyName("Language")]
Language
Language,
/// <summary>
/// Document
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.DocumentBlueprint, typeof(IContent))]
[FriendlyName("DocumentBlueprint")]
[UmbracoUdiType(Constants.UdiEntityType.DocumentBluePrint)]
DocumentBlueprint
}
}
@@ -68,15 +68,14 @@ namespace Umbraco.Core.Models
{
return UmbracoObjectTypeCache.GetOrAdd(umbracoObjectType, types =>
{
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
false);
var type = typeof (UmbracoObjectTypes);
var memberInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof (UmbracoObjectTypeAttribute), false);
if (attributes.Length == 0)
return Guid.Empty;
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
var attribute = (UmbracoObjectTypeAttribute) attributes[0];
if (attribute == null)
return Guid.Empty;
+247 -37
View File
@@ -1,5 +1,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using Umbraco.Core.Cache;
using Umbraco.Core.IO;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
@@ -7,12 +13,81 @@ namespace Umbraco.Core.Models
{
public static class UserExtensions
{
/// <summary>
/// Tries to lookup the user's gravatar to see if the endpoint can be reached, if so it returns the valid URL
/// </summary>
/// <param name="user"></param>
/// <param name="userService"></param>
/// <param name="staticCache"></param>
/// <returns>
/// A list of 5 different sized avatar URLs
/// </returns>
internal static string[] GetCurrentUserAvatarUrls(this IUser user, IUserService userService, ICacheProvider staticCache)
{
//check if the user has explicitly removed all avatars including a gravatar, this will be possible and the value will be "none"
if (user.Avatar == "none")
{
return new string[0];
}
if (user.Avatar.IsNullOrWhiteSpace())
{
var gravatarHash = user.Email.ToMd5();
var gravatarUrl = "https://www.gravatar.com/avatar/" + gravatarHash + "?d=404";
//try gravatar
var gravatarAccess = staticCache.GetCacheItem<bool>("UserAvatar" + user.Id, () =>
{
// Test if we can reach this URL, will fail when there's network or firewall errors
var request = (HttpWebRequest)WebRequest.Create(gravatarUrl);
// Require response within 10 seconds
request.Timeout = 10000;
try
{
using ((HttpWebResponse)request.GetResponse()) { }
}
catch (Exception)
{
// There was an HTTP or other error, return an null instead
return false;
}
return true;
});
if (gravatarAccess)
{
return new[]
{
gravatarUrl + "&s=30",
gravatarUrl + "&s=60",
gravatarUrl + "&s=90",
gravatarUrl + "&s=150",
gravatarUrl + "&s=300"
};
}
return new string[0];
}
//use the custom avatar
var avatarUrl = FileSystemProviderManager.Current.MediaFileSystem.GetUrl(user.Avatar);
return new[]
{
avatarUrl + "?width=30&height=30&mode=crop",
avatarUrl + "?width=60&height=60&mode=crop",
avatarUrl + "?width=90&height=90&mode=crop",
avatarUrl + "?width=150&height=150&mode=crop",
avatarUrl + "?width=300&height=300&mode=crop"
};
}
/// <summary>
/// Returns the culture info associated with this user, based on the language they're assigned to in the back office
/// </summary>
/// <param name="user"></param>
/// <param name="textService"></param>
/// <returns></returns>
/// <returns></returns>
public static CultureInfo GetUserCulture(this IUser user, ILocalizedTextService textService)
{
if (user == null) throw new ArgumentNullException("user");
@@ -25,7 +100,7 @@ namespace Umbraco.Core.Models
try
{
var culture = CultureInfo.GetCultureInfo(userLanguage.Replace("_", "-"));
//TODO: This is a hack because we store the user language as 2 chars instead of the full culture
//TODO: This is a hack because we store the user language as 2 chars instead of the full culture
// which is actually stored in the language files (which are also named with 2 chars!) so we need to attempt
// to convert to a supported full culture
var result = textService.ConvertToSupportedCultureWithRegionCode(culture);
@@ -38,50 +113,59 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Checks if the user has access to the content item based on their start noe
/// </summary>
/// <param name="user"></param>
/// <param name="content"></param>
/// <returns></returns>
internal static bool HasPathAccess(this IUser user, IContent content)
internal static bool HasContentRootAccess(this IUser user, IEntityService entityService)
{
if (user == null) throw new ArgumentNullException("user");
if (content == null) throw new ArgumentNullException("content");
return HasPathAccess(content.Path, user.StartContentId, Constants.System.RecycleBinContent);
return HasPathAccess(Constants.System.Root.ToInvariantString(), user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
}
internal static bool HasPathAccess(string path, int startNodeId, int recycleBinId)
internal static bool HasContentBinAccess(this IUser user, IEntityService entityService)
{
Mandate.ParameterNotNullOrEmpty(path, "path");
return HasPathAccess(Constants.System.RecycleBinContent.ToInvariantString(), user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
}
internal static bool HasMediaRootAccess(this IUser user, IEntityService entityService)
{
return HasPathAccess(Constants.System.Root.ToInvariantString(), user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
}
internal static bool HasMediaBinAccess(this IUser user, IEntityService entityService)
{
return HasPathAccess(Constants.System.RecycleBinMedia.ToInvariantString(), user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
}
internal static bool HasPathAccess(this IUser user, IContent content, IEntityService entityService)
{
return HasPathAccess(content.Path, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
}
internal static bool HasPathAccess(this IUser user, IMedia media, IEntityService entityService)
{
return HasPathAccess(media.Path, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
}
internal static bool HasPathAccess(string path, int[] startNodeIds, int recycleBinId)
{
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value cannot be null or whitespace.", "path");
// check for no access
if (startNodeIds.Length == 0)
return false;
// check for root access
if (startNodeIds.Contains(Constants.System.Root))
return true;
var formattedPath = "," + path + ",";
var formattedStartNodeId = "," + startNodeId.ToInvariantString() + ",";
var formattedRecycleBinId = "," + recycleBinId.ToInvariantString() + ",";
//only users with root access have access to the recycle bin
if (formattedPath.Contains(formattedRecycleBinId))
{
return startNodeId == Constants.System.Root;
}
// only users with root access have access to the recycle bin,
// if the above check didn't pass then access is denied
if (formattedPath.Contains("," + recycleBinId + ","))
return false;
return formattedPath.Contains(formattedStartNodeId);
// check for a start node in the path
return startNodeIds.Any(x => formattedPath.Contains("," + x + ","));
}
/// <summary>
/// Checks if the user has access to the media item based on their start noe
/// </summary>
/// <param name="user"></param>
/// <param name="media"></param>
/// <returns></returns>
internal static bool HasPathAccess(this IUser user, IMedia media)
{
if (user == null) throw new ArgumentNullException("user");
if (media == null) throw new ArgumentNullException("media");
return HasPathAccess(media.Path, user.StartMediaId, Constants.System.RecycleBinMedia);
}
/// <summary>
/// Determines whether this user is an admin.
/// </summary>
@@ -92,7 +176,133 @@ namespace Umbraco.Core.Models
public static bool IsAdmin(this IUser user)
{
if (user == null) throw new ArgumentNullException("user");
return user.UserType.Alias == "admin";
return user.Groups != null && user.Groups.Any(x => x.Alias == Constants.Security.AdminGroupAlias);
}
// calc. start nodes, combining groups' and user's, and excluding what's in the bin
public static int[] CalculateContentStartNodeIds(this IUser user, IEntityService entityService)
{
const string cacheKey = "AllContentStartNodes";
//try to look them up from cache so we don't recalculate
var valuesInUserCache = FromUserCache(user, cacheKey);
if (valuesInUserCache != null) return valuesInUserCache;
var gsn = user.Groups.Where(x => x.StartContentId.HasValue).Select(x => x.StartContentId.Value).Distinct().ToArray();
var usn = user.StartContentIds;
var vals = CombineStartNodes(UmbracoObjectTypes.Document, gsn, usn, entityService);
ToUserCache(user, cacheKey, vals);
return vals;
}
// calc. start nodes, combining groups' and user's, and excluding what's in the bin
public static int[] CalculateMediaStartNodeIds(this IUser user, IEntityService entityService)
{
const string cacheKey = "AllMediaStartNodes";
//try to look them up from cache so we don't recalculate
var valuesInUserCache = FromUserCache(user, cacheKey);
if (valuesInUserCache != null) return valuesInUserCache;
var gsn = user.Groups.Where(x => x.StartMediaId.HasValue).Select(x => x.StartMediaId.Value).Distinct().ToArray();
var usn = user.StartMediaIds;
var vals = CombineStartNodes(UmbracoObjectTypes.Media, gsn, usn, entityService);
ToUserCache(user, cacheKey, vals);
return vals;
}
private static int[] FromUserCache(IUser user, string cacheKey)
{
var entityUser = user as User;
if (entityUser == null) return null;
lock (entityUser.AdditionalDataLock)
{
object allContentStartNodes;
return entityUser.AdditionalData.TryGetValue(cacheKey, out allContentStartNodes)
? allContentStartNodes as int[]
: null;
}
}
private static void ToUserCache(IUser user, string cacheKey, int[] vals)
{
var entityUser = user as User;
if (entityUser == null) return;
lock (entityUser.AdditionalDataLock)
{
entityUser.AdditionalData[cacheKey] = vals;
}
}
private static bool StartsWithPath(string test, string path)
{
return test.StartsWith(path) && test.Length > path.Length && test[path.Length] == ',';
}
private static string GetBinPath(UmbracoObjectTypes objectType)
{
var binPath = Constants.System.Root + ",";
switch (objectType)
{
case UmbracoObjectTypes.Document:
binPath += Constants.System.RecycleBinContent;
break;
case UmbracoObjectTypes.Media:
binPath += Constants.System.RecycleBinMedia;
break;
default:
throw new ArgumentOutOfRangeException("objectType");
}
return binPath;
}
internal static int[] CombineStartNodes(UmbracoObjectTypes objectType, int[] groupSn, int[] userSn, IEntityService entityService)
{
// assume groupSn and userSn each don't contain duplicates
var asn = groupSn.Concat(userSn).Distinct().ToArray();
var paths = entityService.GetAllPaths(objectType, asn).ToDictionary(x => x.Id, x => x.Path);
paths[Constants.System.Root] = Constants.System.Root.ToString(); // entityService does not get that one
var binPath = GetBinPath(objectType);
var lsn = new List<int>();
foreach (var sn in groupSn)
{
string snp;
if (paths.TryGetValue(sn, out snp) == false) continue; // ignore rogue node (no path)
if (StartsWithPath(snp, binPath)) continue; // ignore bin
if (lsn.Any(x => StartsWithPath(snp, paths[x]))) continue; // skip if something above this sn
lsn.RemoveAll(x => StartsWithPath(paths[x], snp)); // remove anything below this sn
lsn.Add(sn);
}
var usn = new List<int>();
foreach (var sn in userSn)
{
if (groupSn.Contains(sn)) continue; // ignore, already there
string snp;
if (paths.TryGetValue(sn, out snp) == false) continue; // ignore rogue node (no path)
if (StartsWithPath(snp, binPath)) continue; // ignore bin
if (usn.Any(x => StartsWithPath(paths[x], snp))) continue; // skip if something below this sn
usn.RemoveAll(x => StartsWithPath(snp, paths[x])); // remove anything above this sn
usn.Add(sn);
}
foreach (var sn in usn)
{
var snp = paths[sn]; // has to be here now
lsn.RemoveAll(x => StartsWithPath(snp, paths[x]) || StartsWithPath(paths[x], snp)); // remove anything above or below this sn
lsn.Add(sn);
}
return lsn.ToArray();
}
}
}
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence.Factories
entity.DisableChangeTracking();
entity.CreateDate = d.createDate;
entity.CreatorId = d.nodeUser;
entity.CreatorId = d.nodeUser == null ? 0 : d.nodeUser;
entity.Id = d.id;
entity.Key = d.uniqueID;
entity.Level = d.level;
@@ -7,47 +7,35 @@ using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class UserFactory
internal static class UserFactory
{
private readonly IUserType _userType;
public UserFactory(IUserType userType)
public static IUser BuildEntity(UserDto dto)
{
_userType = userType;
}
#region Implementation of IEntityFactory<IUser,UserDto>
public IUser BuildEntity(UserDto dto)
{
var guidId = dto.Id.ToGuid();
var user = new User(_userType);
var guidId = dto.Id.ToGuid();
var user = new User(dto.Id, dto.UserName, dto.Email, dto.Login,dto.Password,
dto.UserGroupDtos.Select(x => x.ToReadOnlyGroup()).ToArray(),
dto.UserStartNodeDtos.Where(x => x.StartNodeType == (int)UserStartNodeDto.StartNodeTypeValue.Content).Select(x => x.StartNode).ToArray(),
dto.UserStartNodeDtos.Where(x => x.StartNodeType == (int)UserStartNodeDto.StartNodeTypeValue.Media).Select(x => x.StartNode).ToArray());
try
{
user.DisableChangeTracking();
user.Id = dto.Id;
user.Key = guidId;
user.StartContentId = dto.ContentStartId;
user.StartMediaId = dto.MediaStartId.HasValue ? dto.MediaStartId.Value : -1;
user.RawPasswordValue = dto.Password;
user.Username = dto.Login;
user.Name = dto.UserName;
user.IsLockedOut = dto.NoConsole;
user.IsApproved = dto.Disabled == false;
user.Email = dto.Email;
user.Language = dto.UserLanguage;
user.SecurityStamp = dto.SecurityStampToken;
user.FailedPasswordAttempts = dto.FailedLoginAttempts ?? 0;
user.LastLockoutDate = dto.LastLockoutDate ?? DateTime.MinValue;
user.LastLoginDate = dto.LastLoginDate ?? DateTime.MinValue;
user.LastPasswordChangeDate = dto.LastPasswordChangeDate ?? DateTime.MinValue;
foreach (var app in dto.User2AppDtos)
{
user.AddAllowedSection(app.AppAlias);
}
user.CreateDate = dto.CreateDate;
user.UpdateDate = dto.UpdateDate;
user.Avatar = dto.Avatar;
user.EmailConfirmedDate = dto.EmailConfirmedDate;
user.InvitedDate = dto.InvitedDate;
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -61,48 +49,55 @@ namespace Umbraco.Core.Persistence.Factories
}
}
public UserDto BuildDto(IUser entity)
public static UserDto BuildDto(IUser entity)
{
var dto = new UserDto
{
ContentStartId = entity.StartContentId,
MediaStartId = entity.StartMediaId,
Disabled = entity.IsApproved == false,
Email = entity.Email,
Login = entity.Username,
NoConsole = entity.IsLockedOut,
Password = entity.RawPasswordValue,
UserLanguage = entity.Language,
UserName = entity.Name,
Type = short.Parse(entity.UserType.Id.ToString(CultureInfo.InvariantCulture)),
User2AppDtos = new List<User2AppDto>(),
SecurityStampToken = entity.SecurityStamp,
FailedLoginAttempts = entity.FailedPasswordAttempts,
LastLockoutDate = entity.LastLockoutDate == DateTime.MinValue ? (DateTime?)null : entity.LastLockoutDate,
LastLoginDate = entity.LastLoginDate == DateTime.MinValue ? (DateTime?)null : entity.LastLoginDate,
LastPasswordChangeDate = entity.LastPasswordChangeDate == DateTime.MinValue ? (DateTime?)null : entity.LastPasswordChangeDate,
};
{
Disabled = entity.IsApproved == false,
Email = entity.Email,
Login = entity.Username,
NoConsole = entity.IsLockedOut,
Password = entity.RawPasswordValue,
UserLanguage = entity.Language,
UserName = entity.Name,
SecurityStampToken = entity.SecurityStamp,
FailedLoginAttempts = entity.FailedPasswordAttempts,
LastLockoutDate = entity.LastLockoutDate == DateTime.MinValue ? (DateTime?)null : entity.LastLockoutDate,
LastLoginDate = entity.LastLoginDate == DateTime.MinValue ? (DateTime?)null : entity.LastLoginDate,
LastPasswordChangeDate = entity.LastPasswordChangeDate == DateTime.MinValue ? (DateTime?)null : entity.LastPasswordChangeDate,
CreateDate = entity.CreateDate,
UpdateDate = entity.UpdateDate,
Avatar = entity.Avatar,
EmailConfirmedDate = entity.EmailConfirmedDate,
InvitedDate = entity.InvitedDate
};
foreach (var app in entity.AllowedSections)
foreach (var startNodeId in entity.StartContentIds)
{
var appDto = new User2AppDto
{
AppAlias = app
};
if (entity.HasIdentity)
dto.UserStartNodeDtos.Add(new UserStartNodeDto
{
appDto.UserId = (int) entity.Id;
}
StartNode = startNodeId,
StartNodeType = (int)UserStartNodeDto.StartNodeTypeValue.Content,
UserId = entity.Id
});
}
dto.User2AppDtos.Add(appDto);
foreach (var startNodeId in entity.StartMediaIds)
{
dto.UserStartNodeDtos.Add(new UserStartNodeDto
{
StartNode = startNodeId,
StartNodeType = (int)UserStartNodeDto.StartNodeTypeValue.Media,
UserId = entity.Id
});
}
if (entity.HasIdentity)
{
dto.Id = entity.Id.SafeCast<int>();
}
return dto;
}
#endregion
}
}
}
@@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal static class UserGroupFactory
{
public static IUserGroup BuildEntity(UserGroupDto dto)
{
var userGroup = new UserGroup(dto.UserCount, dto.Alias, dto.Name,
dto.DefaultPermissions.IsNullOrWhiteSpace()
? Enumerable.Empty<string>()
: dto.DefaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToList(),
dto.Icon);
try
{
userGroup.DisableChangeTracking();
userGroup.Id = dto.Id;
userGroup.CreateDate = dto.CreateDate;
userGroup.UpdateDate = dto.UpdateDate;
userGroup.StartContentId = dto.StartContentId;
userGroup.StartMediaId = dto.StartMediaId;
if (dto.UserGroup2AppDtos != null)
{
foreach (var app in dto.UserGroup2AppDtos)
{
userGroup.AddAllowedSection(app.AppAlias);
}
}
userGroup.ResetDirtyProperties(false);
return userGroup;
}
finally
{
userGroup.EnableChangeTracking();
}
}
public static UserGroupDto BuildDto(IUserGroup entity)
{
var dto = new UserGroupDto
{
Alias = entity.Alias,
DefaultPermissions = entity.Permissions == null ? "" : string.Join("", entity.Permissions),
Name = entity.Name,
UserGroup2AppDtos = new List<UserGroup2AppDto>(),
CreateDate = entity.CreateDate,
UpdateDate = entity.UpdateDate,
Icon = entity.Icon,
StartMediaId = entity.StartMediaId,
StartContentId = entity.StartContentId
};
foreach (var app in entity.AllowedSections)
{
var appDto = new UserGroup2AppDto
{
AppAlias = app
};
if (entity.HasIdentity)
{
appDto.UserGroupId = entity.Id;
}
dto.UserGroup2AppDtos.Add(appDto);
}
if (entity.HasIdentity)
dto.Id = short.Parse(entity.Id.ToString());
return dto;
}
}
}
@@ -1,35 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class UserSectionFactory
{
private readonly IUser _user;
public UserSectionFactory(IUser user)
{
_user = user;
}
public IEnumerable<string> BuildEntity(IEnumerable<User2AppDto> dto)
{
return dto.Select(x => x.AppAlias);
}
public IEnumerable<User2AppDto> BuildDto(IEnumerable<string> entity)
{
return entity.Select(x => new User2AppDto
{
//NOTE: We're force casting to int here! this might not work in the future
UserId = (int)_user.Id,
AppAlias = x
});
}
}
}
@@ -1,54 +0,0 @@
using System.Globalization;
using System.Linq;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class UserTypeFactory
{
#region Implementation of IEntityFactory<IUserType,UserTypeDto>
public IUserType BuildEntity(UserTypeDto dto)
{
var userType = new UserType();
try
{
userType.DisableChangeTracking();
userType.Alias = dto.Alias;
userType.Id = dto.Id;
userType.Name = dto.Name;
userType.Permissions = dto.DefaultPermissions.IsNullOrWhiteSpace()
? Enumerable.Empty<string>()
: dto.DefaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture));
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
userType.ResetDirtyProperties(false);
return userType;
}
finally
{
userType.EnableChangeTracking();
}
}
public UserTypeDto BuildDto(IUserType entity)
{
var userType = new UserTypeDto
{
Alias = entity.Alias,
DefaultPermissions = entity.Permissions == null ? "" : string.Join("", entity.Permissions),
Name = entity.Name
};
if(entity.HasIdentity)
userType.Id = short.Parse(entity.Id.ToString());
return userType;
}
#endregion
}
}
@@ -0,0 +1,35 @@
using System.Collections.Concurrent;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Mappers
{
[MapperFor(typeof(IIdentityUserLogin))]
[MapperFor(typeof(IdentityUserLogin))]
public sealed class ExternalLoginMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public ExternalLoginMapper()
{
BuildMap();
}
#region Overrides of BaseMapper
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
{
get { return PropertyInfoCacheInstance; }
}
internal 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,44 +1,43 @@
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Mappers
{
/// <summary>
/// Represents a <see cref="UserType"/> to DTO mapper used to translate the properties of the public api
/// implementation to that of the database's DTO as sql: [tableName].[columnName].
/// </summary>
[MapperFor(typeof(IUserType))]
[MapperFor(typeof(UserType))]
public sealed class UserTypeMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
//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 UserTypeMapper()
{
BuildMap();
}
#region Overrides of BaseMapper
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
{
get { return PropertyInfoCacheInstance; }
}
internal override void BuildMap()
{
CacheMap<UserType, UserTypeDto>(src => src.Id, dto => dto.Id);
CacheMap<UserType, UserTypeDto>(src => src.Alias, dto => dto.Alias);
CacheMap<UserType, UserTypeDto>(src => src.Name, dto => dto.Name);
CacheMap<UserType, UserTypeDto>(src => src.Permissions, dto => dto.DefaultPermissions);
}
#endregion
}
using System.Collections.Concurrent;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Mappers
{
/// <summary>
/// Represents a <see cref="UserGroup"/> to DTO mapper used to translate the properties of the public api
/// implementation to that of the database's DTO as sql: [tableName].[columnName].
/// </summary>
[MapperFor(typeof(IUserGroup))]
[MapperFor(typeof(UserGroup))]
public sealed class UserGroupMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
//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()
{
BuildMap();
}
#region Overrides of BaseMapper
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
{
get { return PropertyInfoCacheInstance; }
}
internal 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,41 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Mappers
{
[MapperFor(typeof(IIdentityUserLogin))]
[MapperFor(typeof(IdentityUserLogin))]
public sealed class ExternalLoginMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public ExternalLoginMapper()
{
BuildMap();
}
#region Overrides of BaseMapper
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
{
get { return PropertyInfoCacheInstance; }
}
internal 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
}
[MapperFor(typeof(IUser))]
[MapperFor(typeof(User))]
public sealed class UserMapper : BaseMapper
@@ -64,13 +34,16 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap<User, UserDto>(src => src.RawPasswordValue, dto => dto.Password);
CacheMap<User, UserDto>(src => src.Name, dto => dto.UserName);
//NOTE: This column in the db is *not* used!
//CacheMap<User, UserDto>(src => src.DefaultPermissions, dto => dto.DefaultPermissions);
CacheMap<User, UserDto>(src => src.StartMediaId, dto => dto.MediaStartId);
CacheMap<User, UserDto>(src => src.StartContentId, dto => dto.ContentStartId);
//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.UserType, dto => dto.Type);
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);
}
#endregion
@@ -48,14 +48,19 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
CreateUmbracoUserData();
}
if (tableName.Equals("umbracoUserType"))
if (tableName.Equals("umbracoUserGroup"))
{
CreateUmbracoUserTypeData();
CreateUmbracoUserGroupData();
}
if (tableName.Equals("umbracoUser2app"))
if (tableName.Equals("umbracoUser2UserGroup"))
{
CreateUmbracoUser2AppData();
CreateUmbracoUser2UserGroupData();
}
if (tableName.Equals("umbracoUserGroup2App"))
{
CreateUmbracoUserGroup2AppData();
}
if (tableName.Equals("cmsPropertyTypeGroup"))
@@ -164,27 +169,34 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
private void CreateUmbracoUserData()
{
_database.Insert("umbracoUser", "id", false, new UserDto { Id = 0, Disabled = false, NoConsole = false, Type = 1, ContentStartId = -1, MediaStartId = -1, UserName = "Administrator", Login = "admin", Password = "default", Email = "", UserLanguage = "en" });
//_database.Update<UserDto>("SET id = @IdAfter WHERE id = @IdBefore AND userLogin = @Login", new { IdAfter = 0, IdBefore = 1, Login = "admin" });
_database.Insert("umbracoUser", "id", false, new UserDto { Id = 0, Disabled = false, NoConsole = false, UserName = "Administrator", Login = "admin", Password = "default", Email = "", UserLanguage = "en", CreateDate = DateTime.Now, UpdateDate = DateTime.Now });
}
private void CreateUmbracoUserTypeData()
private void CreateUmbracoUserGroupData()
{
_database.Insert("umbracoUserType", "id", false, new UserTypeDto { Id = 1, Alias = "admin", Name = "Administrators", DefaultPermissions = "CADMOSKTPIURZ:5F7" });
_database.Insert("umbracoUserType", "id", false, new UserTypeDto { Id = 2, Alias = "writer", Name = "Writer", DefaultPermissions = "CAH:F" });
_database.Insert("umbracoUserType", "id", false, new UserTypeDto { Id = 3, Alias = "editor", Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5F" });
_database.Insert("umbracoUserType", "id", false, new UserTypeDto { Id = 4, Alias = "translator", Name = "Translator", DefaultPermissions = "AF" });
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 1, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.AdminGroupAlias, Name = "Administrators", DefaultPermissions = "CADMOSKTPIURZ:5F7ï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-medal" });
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = "writer", Name = "Writers", DefaultPermissions = "CAH:F", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" });
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = "editor", Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5Fï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" });
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = "translator", Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" });
}
private void CreateUmbracoUser2AppData()
private void CreateUmbracoUser2UserGroupData()
{
_database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Content });
_database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Developer });
_database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Media });
_database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Members });
_database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Settings });
_database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Users });
_database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Forms });
_database.Insert(new User2UserGroupDto { UserGroupId = 1, UserId = 0 });
}
private void CreateUmbracoUserGroup2AppData()
{
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Content });
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Media });
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Settings });
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Developer });
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Users });
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Members });
_database.Insert(new UserGroup2AppDto { UserGroupId = 2, AppAlias = Constants.Applications.Content });
_database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Content });
_database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Media });
_database.Insert(new UserGroup2AppDto { UserGroupId = 4, AppAlias = Constants.Applications.Translation });
}
private void CreateCmsPropertyTypeGroupData()
@@ -67,15 +67,15 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{28, typeof (TagDto)},
{29, typeof (TagRelationshipDto)},
{31, typeof (UserTypeDto)},
// Removed in 7.6 {31, typeof (UserTypeDto)},
{32, typeof (UserDto)},
{33, typeof (TaskTypeDto)},
{34, typeof (TaskDto)},
{35, typeof (ContentType2ContentTypeDto)},
{36, typeof (ContentTypeAllowedContentTypeDto)},
{37, typeof (User2AppDto)},
// Removed in 7.6 {37, typeof (User2AppDto)},
{38, typeof (User2NodeNotifyDto)},
{39, typeof (User2NodePermissionDto)},
// Removed in 7.6 {39, typeof (User2NodePermissionDto)},
{40, typeof (ServerRegistrationDto)},
{41, typeof (AccessDto)},
{42, typeof (AccessRuleDto)},
@@ -85,7 +85,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//46, removed: UmbracoDeployChecksumDto
//47, removed: UmbracoDeployDependencyDto
{48, typeof (RedirectUrlDto) },
{49, typeof (LockDto) }
{49, typeof (LockDto) },
{50, typeof (UserGroupDto) },
{51, typeof (User2UserGroupDto) },
{52, typeof (UserGroup2NodePermissionDto) },
{53, typeof (UserGroup2AppDto) },
{54, typeof (UserStartNodeDto) },
};
#endregion
@@ -142,6 +142,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
return new Version(7, 5, 0);
}
//if the error is for umbracoUserGroup it must be the previous version to 7.7 since that is when it is added
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoUserStartNode"))))
{
return new Version(7, 6, 0);
}
return UmbracoVersion.Current;
}
@@ -1,10 +1,12 @@
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter;
using Umbraco.Core.Persistence.Migrations.Syntax.Create;
using Umbraco.Core.Persistence.Migrations.Syntax.Delete;
using Umbraco.Core.Persistence.Migrations.Syntax.Execute;
using Umbraco.Core.Persistence.Migrations.Syntax.Update;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations
@@ -29,6 +31,11 @@ namespace Umbraco.Core.Persistence.Migrations
get { return new DeleteBuilder(this, _sqlSyntax); }
}
public IUpdateBuilder Update
{
get { return new UpdateBuilder(this, _sqlSyntax); }
}
public IAlterSyntaxBuilder Alter
{
get { return new AlterSyntaxBuilder(this, _sqlSyntax); }
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer
return null;
localContext.Delete.Table(umbracoRedirectUrlTableName);
}
localContext.Create.Table(umbracoRedirectUrlTableName)
.WithColumn("id").AsGuid().NotNullable().PrimaryKey("PK_" + umbracoRedirectUrlTableName)
.WithColumn("createDateUtc").AsDateTime().NotNullable()
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZero
{
[Migration("7.7.0", 5, Constants.System.UmbracoMigrationName)]
public class AddIndexToDictionaryKeyColumn : MigrationBase
{
public AddIndexToDictionaryKeyColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
Execute.Code(database =>
{
//Now we need to check if we can actually do this because we won't be able to if there's data in there that is too long
var colLen = (SqlSyntax is MySqlSyntaxProvider)
? database.ExecuteScalar<int?>(string.Format("select max(LENGTH({0})) from cmsDictionary", SqlSyntax.GetQuotedColumnName("key")))
: database.ExecuteScalar<int?>(string.Format("select max(datalength({0})) from cmsDictionary", SqlSyntax.GetQuotedColumnName("key")));
if (colLen < 900 == false && colLen != null)
{
return null;
}
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsDictionary_key")) == false)
{
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
//we can apply the index
localContext.Create.Index("IX_cmsDictionary_key").OnTable("cmsDictionary")
.OnColumn("key")
.Ascending()
.WithOptions()
.NonClustered();
return localContext.GetSql();
}
return null;
});
}
public override void Down()
{
Delete.Index("IX_cmsDictionary_key").OnTable("cmsDictionary");
}
}
}
@@ -0,0 +1,184 @@
using System;
using System.Data;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZero
{
[Migration("7.7.0", 1, Constants.System.UmbracoMigrationName)]
public class AddUserGroupTables : MigrationBase
{
public AddUserGroupTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
if (AddNewTables(tables))
{
MigrateUserPermissions();
MigrateUserTypesToGroups();
DeleteOldTables(tables, constraints);
SetDefaultIcons();
}
}
private void SetDefaultIcons()
{
Execute.Sql(string.Format("UPDATE umbracoUserGroup SET icon = '{0}' WHERE userGroupAlias = '{1}'", "", Constants.Security.AdminGroupAlias));
Execute.Sql(string.Format("UPDATE umbracoUserGroup SET icon = '{0}' WHERE userGroupAlias = '{1}'", "icon-edit", "writer"));
Execute.Sql(string.Format("UPDATE umbracoUserGroup SET icon = '{0}' WHERE userGroupAlias = '{1}'", "icon-tools", "editor"));
Execute.Sql(string.Format("UPDATE umbracoUserGroup SET icon = '{0}' WHERE userGroupAlias = '{1}'", "icon-globe", "translator"));
}
private bool AddNewTables(string[] tables)
{
var updated = false;
if (tables.InvariantContains("umbracoUserGroup") == false)
{
Create.Table<UserGroupDto>();
updated = true;
}
if (tables.InvariantContains("umbracoUser2UserGroup") == false)
{
Create.Table<User2UserGroupDto>();
updated = true;
}
if (tables.InvariantContains("umbracoUserGroup2App") == false)
{
Create.Table<UserGroup2AppDto>();
updated = true;
}
if (tables.InvariantContains("umbracoUserGroup2NodePermission") == false)
{
Create.Table<UserGroup2NodePermissionDto>();
updated = true;
}
return updated;
}
private void MigrateUserTypesToGroups()
{
// Create a user group for each user type
Execute.Sql(@"INSERT INTO umbracoUserGroup (userGroupAlias, userGroupName, userGroupDefaultPermissions)
SELECT userTypeAlias, userTypeName, userTypeDefaultPermissions
FROM umbracoUserType");
// Add each user to the group created from their type
Execute.Sql(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId)
SELECT u.id, ug.id
FROM umbracoUser u
INNER JOIN umbracoUserType ut ON ut.id = u.userType
INNER JOIN umbracoUserGroup ug ON ug.userGroupAlias = ut.userTypeAlias");
// Add the built-in administrator account to all apps
Execute.Sql(@"INSERT INTO umbracoUserGroup2app (userGroupId,app)
SELECT ug.id, app
FROM umbracoUserGroup ug
INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id
INNER JOIN umbracoUser u ON u.id = u2ug.userId
INNER JOIN umbracoUser2app u2a ON u2a." + SqlSyntax.GetQuotedColumnName("user") + @" = u.id
WHERE u.id = 0");
// Rename some groups for consistency (plural form)
Execute.Sql("UPDATE umbracoUserGroup SET userGroupName = 'Writers' WHERE userGroupAlias = 'writer'");
Execute.Sql("UPDATE umbracoUserGroup SET userGroupName = 'Translators' WHERE userGroupAlias = 'translator'");
//Ensure all built in groups have a start node of -1
Execute.Sql("UPDATE umbracoUserGroup SET startContentId = -1 WHERE userGroupAlias = 'editor'");
Execute.Sql("UPDATE umbracoUserGroup SET startMediaId = -1 WHERE userGroupAlias = 'editor'");
Execute.Sql("UPDATE umbracoUserGroup SET startContentId = -1 WHERE userGroupAlias = 'writer'");
Execute.Sql("UPDATE umbracoUserGroup SET startMediaId = -1 WHERE userGroupAlias = 'writer'");
Execute.Sql("UPDATE umbracoUserGroup SET startContentId = -1 WHERE userGroupAlias = 'translator'");
Execute.Sql("UPDATE umbracoUserGroup SET startMediaId = -1 WHERE userGroupAlias = 'translator'");
Execute.Sql("UPDATE umbracoUserGroup SET startContentId = -1 WHERE userGroupAlias = 'admin'");
Execute.Sql("UPDATE umbracoUserGroup SET startMediaId = -1 WHERE userGroupAlias = 'admin'");
}
private void MigrateUserPermissions()
{
// Create user group records for all non-admin users that have specific permissions set
Execute.Sql(@"INSERT INTO umbracoUserGroup(userGroupAlias, userGroupName)
SELECT userName + 'Group', 'Group for ' + userName
FROM umbracoUser
WHERE (id IN (
SELECT " + SqlSyntax.GetQuotedColumnName("user") + @"
FROM umbracoUser2app
) OR id IN (
SELECT userid
FROM umbracoUser2NodePermission
))
AND id > 0");
// Associate those groups with the users
Execute.Sql(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId)
SELECT u.id, ug.id
FROM umbracoUser u
INNER JOIN umbracoUserGroup ug ON ug.userGroupAlias = userName + 'Group'");
// Create node permissions on the groups
Execute.Sql(@"INSERT INTO umbracoUserGroup2NodePermission (userGroupId,nodeId,permission)
SELECT ug.id, nodeId, permission
FROM umbracoUserGroup ug
INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id
INNER JOIN umbracoUser u ON u.id = u2ug.userId
INNER JOIN umbracoUser2NodePermission u2np ON u2np.userId = u.id
WHERE ug.userGroupAlias NOT IN (
SELECT userTypeAlias
FROM umbracoUserType
)");
// Create app permissions on the groups
Execute.Sql(@"INSERT INTO umbracoUserGroup2app (userGroupId,app)
SELECT ug.id, app
FROM umbracoUserGroup ug
INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id
INNER JOIN umbracoUser u ON u.id = u2ug.userId
INNER JOIN umbracoUser2app u2a ON u2a." + SqlSyntax.GetQuotedColumnName("user") + @" = u.id
WHERE ug.userGroupAlias NOT IN (
SELECT userTypeAlias
FROM umbracoUserType
)");
}
private void DeleteOldTables(string[] tables, Tuple<string, string, string>[] constraints)
{
if (tables.InvariantContains("umbracoUser2App"))
{
Delete.Table("umbracoUser2App");
}
if (tables.InvariantContains("umbracoUser2NodePermission"))
{
Delete.Table("umbracoUser2NodePermission");
}
if (tables.InvariantContains("umbracoUserType") && tables.InvariantContains("umbracoUser"))
{
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_umbracoUser_umbracoUserType_id")))
{
Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser");
}
Delete.Column("userType").FromTable("umbracoUser");
Delete.Table("umbracoUserType");
}
}
public override void Down()
{ }
}
}

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