diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0a94757b91..0e87f9824d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,7 @@ Remember, we're a friendly bunch and are happy with whatever contribution you mi * [Working with the source code](#working-with-the-source-code) * [What branch should I target for my contributions?](#what-branch-should-i-target-for-my-contributions) * [Building Umbraco from source code](#building-umbraco-from-source-code) + * [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository) [How do I even begin?](#how-do-i-even-begin) @@ -146,6 +147,29 @@ Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 ([the commun After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser, follow the directions there to get a working Umbraco install up and running. +### Keeping your Umbraco fork in sync with the main repository + +We recommend you sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier. + +Also, if you've submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course. + +To sync your fork with this original one, you'll have to add the upstream url, you only have to do this once: + +``` +git remote add upstream https://github.com/umbraco/Umbraco-CMS.git +``` + +Then when you want to get the changes from the main repository: + +``` +git fetch upstream +git rebase upstream/dev-v7 +``` + +In this command we're syncing with the `dev-v7` branch, but you can of course choose another one if needed. + +(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated)) + ## How do I even begin? Great question! The short version goes like this: diff --git a/build/NuSpecs/tools/ReadmeUpgrade.txt b/build/NuSpecs/tools/ReadmeUpgrade.txt index e85b22a902..2b6da733a1 100644 --- a/build/NuSpecs/tools/ReadmeUpgrade.txt +++ b/build/NuSpecs/tools/ReadmeUpgrade.txt @@ -8,7 +8,7 @@ ---------------------------------------------------- -*** IMPORTANT NOTICE FOR 7.7 UPGRADES *** +*** IMPORTANT NOTICE FOR UPGRADES FROM VERSIONS BELOW 7.7.0 *** Be sure to read the version specific upgrade information before proceeding: https://our.umbraco.org/documentation/Getting-Started/Setup/Upgrading/version-specific#version-7-7-0 diff --git a/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs b/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs index 022b15c8be..fcb5547027 100644 --- a/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs +++ b/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs @@ -37,15 +37,15 @@ namespace Umbraco.Core.Security var username = identity.GetUserName(); var session = identity.FindFirstValue(Constants.Security.SessionIdClaimType); var securityStamp = identity.FindFirstValue(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType); - var startContentId = identity.FindFirstValue(Constants.Security.StartContentNodeIdClaimType); + var startContentId = identity.FindFirstValue(Constants.Security.StartContentNodeIdClaimType); var startMediaId = identity.FindFirstValue(Constants.Security.StartMediaNodeIdClaimType); var culture = identity.FindFirstValue(ClaimTypes.Locality); - var id = identity.FindFirstValue(ClaimTypes.NameIdentifier); + var id = identity.FindFirstValue(ClaimTypes.NameIdentifier); var realName = identity.FindFirstValue(ClaimTypes.GivenName); - if (username == null || startContentId == null || startMediaId == null - || culture == null || id == null + if (username == null || startContentId == null || startMediaId == null + || culture == null || id == null || realName == null || session == null) throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since there are missing required claims"); @@ -62,7 +62,7 @@ namespace Umbraco.Core.Security catch (Exception e) { throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since the data is not formatted correctly - either content or media start Ids could not be parsed as JSON", e); - } + } var roles = identity.FindAll(x => x.Type == DefaultRoleClaimType).Select(role => role.Value).ToList(); var allowedApps = identity.FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToList(); @@ -165,7 +165,7 @@ namespace Umbraco.Core.Security { foreach (var claim in claimsIdentity.Claims) { - //In one special case we will replace a claim if it exists already and that is the + //In one special case we will replace a claim if it exists already and that is the // Forms auth claim for name which automatically gets added TryRemoveClaim(FindFirst(x => x.Type == claim.Type && x.Issuer == "Forms")); @@ -187,15 +187,15 @@ namespace Umbraco.Core.Security { ClaimTypes.NameIdentifier, //id ClaimTypes.Name, //username - ClaimTypes.GivenName, + ClaimTypes.GivenName, Constants.Security.StartContentNodeIdClaimType, - Constants.Security.StartMediaNodeIdClaimType, - ClaimTypes.Locality, + Constants.Security.StartMediaNodeIdClaimType, + ClaimTypes.Locality, Constants.Security.SessionIdClaimType, Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType }; } - } + } /// /// Adds claims based on the UserData data @@ -222,23 +222,19 @@ namespace Umbraco.Core.Security AddClaim(new Claim(ClaimTypes.Locality, Culture, ClaimValueTypes.String, Issuer, Issuer, this)); if (HasClaim(x => x.Type == Constants.Security.SessionIdClaimType) == false && SessionId.IsNullOrWhiteSpace() == false) - { AddClaim(new Claim(Constants.Security.SessionIdClaimType, SessionId, ClaimValueTypes.String, Issuer, Issuer, this)); - //The security stamp claim is also required... this is because this claim type is hard coded - // by the SecurityStampValidator, see: https://katanaproject.codeplex.com/workitem/444 - if (HasClaim(x => x.Type == Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType) == false) - { - AddClaim(new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, SecurityStamp, ClaimValueTypes.String, Issuer, Issuer, this)); - } - } + //The security stamp claim is also required... this is because this claim type is hard coded + // by the SecurityStampValidator, see: https://katanaproject.codeplex.com/workitem/444 + if (HasClaim(x => x.Type == Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType) == false) + AddClaim(new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, SecurityStamp, ClaimValueTypes.String, Issuer, Issuer, this)); //Add each app as a separate claim if (HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false) { foreach (var application in AllowedApplications) { - AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, this)); + AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, this)); } } @@ -253,8 +249,8 @@ namespace Umbraco.Core.Security } } - - + + } protected internal UserData UserData { get; private set; } @@ -332,4 +328,4 @@ namespace Umbraco.Core.Security } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index b26ca2aede..5cc4817142 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -280,7 +280,6 @@ .umb-grid .umb-control { position: relative; display: block; - overflow: hidden; margin-left: 10px; margin-right: 10px; margin-bottom: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 9bc287fdd9..2d317fa4a0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -40,6 +40,16 @@ padding: 10px; } +.umb-contentpicker__min-max-help { + font-size: 13px; + margin-top: 5px; + color: @gray-4; +} + +.show-validation .umb-contentpicker__min-max-help { + display: none; +} + .umb-contentpicker small { &:not(:last-child) { @@ -109,22 +119,22 @@ ul.color-picker li a { } /* pre-value editor */ -/*.control-group.color-picker-preval:before { - content: ""; - display: inline-block; - vertical-align: middle; - height: 100%; -}*/ - -/*.control-group.color-picker-preval div.thumbnail { - display: inline-block; +/*.control-group.color-picker-preval:before { + content: ""; + display: inline-block; vertical-align: middle; -}*/ -.control-group.color-picker-preval div.color-picker-prediv { - display: inline-block; + height: 100%; +}*/ + +/*.control-group.color-picker-preval div.thumbnail { + display: inline-block; + vertical-align: middle; +}*/ +.control-group.color-picker-preval div.color-picker-prediv { + display: inline-block; width: 60%; -} - +} + .control-group.color-picker-preval pre { display: inline; margin-right: 20px; @@ -136,18 +146,18 @@ ul.color-picker li a { vertical-align: middle; } -.control-group.color-picker-preval btn { +.control-group.color-picker-preval btn { //vertical-align: middle; -} +} -.control-group.color-picker-preval input[type="text"] { - min-width: 40%; - width: 40%; - display: inline-block; - margin-right: 20px; +.control-group.color-picker-preval input[type="text"] { + min-width: 40%; + width: 40%; + display: inline-block; + margin-right: 20px; margin-top: 1px; -} - +} + .control-group.color-picker-preval label { border: solid @white 1px; padding: 6px; @@ -203,10 +213,22 @@ ul.color-picker li a { -.umb-thumbnails{ - position: relative; +.umb-thumbnails { + position: relative; + display: flex; + -ms-flex-direction: row; + -webkit-flex-direction: row; + flex-direction: row; + -ms-flex-wrap: wrap; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; + justify-content: flex-start; } +.umb-thumbnails > li.icon { + width: 14%; + text-align: center; +} .umb-thumbnails i{margin: auto;} .umb-thumbnails a{ diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 02035b416a..d72a9085ec 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -100,6 +100,27 @@ .color-green, .color-green i{color: @green-d1 !important;} .color-yellow, .color-yellow i{color: @yellow-d1 !important;} +/* Colors based on http://zavoloklom.github.io/material-design-color-palette/colors.html */ +.color-black, .color-black i { color: #000 !important; } +.color-blue-grey, .color-blue-grey i { color: #607d8b !important; } +.color-grey, .color-grey i { color: #9e9e9e !important; } +.color-brown, .color-brown i { color: #795548 !important; } +.color-blue, .color-blue i { color: #2196f3 !important; } +.color-light-blue, .color-light-blue i {color: #03a9f4 !important; } +.color-cyan, .color-cyan i { color: #00bcd4 !important; } +.color-green, .color-green i { color: #4caf50 !important; } +.color-light-green, .color-light-green i {color: #8bc34a !important; } +.color-lime, .color-lime i { color: #cddc39 !important; } +.color-yellow, .color-yellow i { color: #ffeb3b !important; } +.color-amber, .color-amber i { color: #ffc107 !important; } +.color-orange, .color-orange i { color: #ff9800 !important; } +.color-deep-orange, .color-deep-orange i { color: #ff5722 !important; } +.color-red, .color-red i { color: #f44336 !important; } +.color-pink, .color-pink i { color: #e91e63 !important; } +.color-purple,.color-purple i { color: #9c27b0 !important; } +.color-deep-purple, .color-deep-purple i { color: #673ab7 !important; } +.color-indigo, .color-indigo i { color: #3f51b5 !important; } + // Scaffolding // ------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js index ec1ad6e663..7f7eed8e4c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js @@ -7,14 +7,13 @@ angular.module("umbraco") $scope.icons = icons; }); - $scope.submitClass = function (icon) { + $scope.submitClass = function(icon){ if($scope.color) { $scope.submit(icon + " " + $scope.color); } - else { - $scope.submit(icon); + else { + $scope.submit(icon); } }; - } ); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html index d962f8eec4..b2395228d8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html @@ -16,24 +16,25 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html index 9b60e5e92f..8c5c7fcc4a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html @@ -47,7 +47,17 @@
- {{item[column.alias]}} + + +
+ {{item[column.alias]}} +
+ + + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 1755840cc8..0ed8657c59 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -20,7 +20,7 @@ - Add +
+ + + + Add between {{model.config.minNumber}} and {{model.config.maxNumber}} items + + You can only have {{model.config.maxNumber}} items selected + + + + + + Add {{model.config.minNumber - renderModel.length}} item(s) + + You can only have {{model.config.maxNumber}} items selected + + + + + + Add up to {{model.config.maxNumber}} items + + You can only have {{model.config.maxNumber}} items selected + + + + + + Add at least {{model.config.minNumber}} item(s) + + +
+ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js index 799cc5894c..00e6c6edb4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js @@ -30,15 +30,14 @@ vm.dragLeave = dragLeave; vm.onFilesQueue = onFilesQueue; vm.onUploadComplete = onUploadComplete; + markAsSensitive(); function activate() { - if ($scope.entityType === 'media') { mediaTypeHelper.getAllowedImagetypes(vm.nodeId).then(function (types) { vm.acceptedMediatypes = types; }); } - } function selectAll($event) { @@ -87,6 +86,27 @@ $scope.getContent($scope.contentId); } + function markAsSensitive() { + angular.forEach($scope.options.includeProperties, function (option) { + option.isSensitive = false; + + angular.forEach($scope.items, + function (item) { + + angular.forEach(item.properties, + function (property) { + + if (option.alias === property.alias) { + option.isSensitive = property.isSensitive; + } + + }); + + }); + + }); + } + activate(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js index 53424a7d6e..7a3abdd0e6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js @@ -46,6 +46,7 @@ function MarkdownEditorController($scope, $element, assetsService, dialogService // init the md editor after this digest because the DOM needs to be ready first // so run the init on a timeout $timeout(function () { + $scope.markdownEditorInitComplete = false; var converter2 = new Markdown.Converter(); var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias); editor2.run(); @@ -59,7 +60,12 @@ function MarkdownEditorController($scope, $element, assetsService, dialogService editor2.hooks.set("onPreviewRefresh", function () { // We must manually update the model as there is no way to hook into the markdown editor events without exstensive edits to the library. if ($scope.model.value !== $("textarea", $element).val()) { - angularHelper.getCurrentForm($scope).$setDirty(); + if ($scope.markdownEditorInitComplete) { + //only set dirty after init load to avoid "unsaved" dialogue when we don't want it + angularHelper.getCurrentForm($scope).$setDirty(); + } else { + $scope.markdownEditorInitComplete = true; + } $scope.model.value = $("textarea", $element).val(); } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index 24d5e7830e..0a6bededae 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -359,7 +359,8 @@ angular.module("umbraco") //this is instead of doing a watch on the model.value = faster $scope.model.onValueChanged = function (newVal, oldVal) { //update the display val again if it has changed from the server; - tinyMceEditor.setContent(newVal, { format: 'raw' }); + //uses an empty string in the editor when the value is null + tinyMceEditor.setContent(newVal || "", { format: 'raw' }); //we need to manually fire this event since it is only ever fired based on loading from the DOM, this // is required for our plugins listening to this event to execute tinyMceEditor.fire('LoadContent', null); diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 729f60774a..787fa211b4 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -661,7 +661,7 @@ Baggrundsfarve Fed - Tekst farve + Tekstfarve Skrifttype Tekst @@ -1259,9 +1259,9 @@ Mange hilsner fra Umbraco robotten Typografi - Vælg, hvilke typografiværdier, en redaktør kan ændre + Vælg hvilke typografiværdier en redaktør kan ændre - Indstillinger gemmes kun, hvis den indtaste json-konfiguration er gyldig + Indstillinger gemmes kun, hvis den indtastede json-konfiguration er gyldig Tillad alle editorer Tillad alle rækkekonfigurationer @@ -1272,7 +1272,7 @@ Mange hilsner fra Umbraco robotten er tilføjet Maksimalt emner - Efterlad blank eller sat til 0 ubegrænset for + Efterlad blank eller sæt til 0 for ubegrænset @@ -1282,7 +1282,7 @@ Mange hilsner fra Umbraco robotten Tilføj ny fane Tilføj endnu en fane Nedarvet fra - Tilføj property + Tilføj egenskab Påkrævet label Aktiver listevisning @@ -1473,7 +1473,6 @@ Mange hilsner fra Umbraco robotten har endnu ikke logget ind Gammelt kodeord Adgangskode - Fjern billede Nulstil kodeord Dit kodeord er blevet ændret! Bekræft venligst dit nye kodeord diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index e719445c63..0137e5d0e0 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -229,6 +229,7 @@ Remove this text box Content root This value is hidden. If you need access to view this value please contact your website administrator. + This value is hidden. Create a new Content Template from '%0%' @@ -662,7 +663,20 @@ Yellow Orange Blue + Blue Grey + Grey + Brown + Light Blue + Cyan + Light Green + Lime + Amber + Deep Orange Red + Pink + Purple + Deep Purple + Indigo diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index c9fbce435d..1ff807b79b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -229,6 +229,7 @@ Remove this text box Content root This value is hidden. If you need access to view this value please contact your website administrator. + This value is hidden. Create a new Content Template from '%0%' @@ -660,7 +661,20 @@ Yellow Orange Blue + Blue Grey + Grey + Brown + Light Blue + Cyan + Light Green + Lime + Amber + Deep Orange Red + Pink + Purple + Deep Purple + Indigo Add tab diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index a9e02a0fa8..8ff7988a9a 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -1,29 +1,18 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; -using System.Reflection; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using System.Web; using System.Web.Http; using System.Web.Http.ModelBinding; using System.Web.Security; using AutoMapper; -using Examine.LuceneEngine.SearchCriteria; -using Examine.SearchCriteria; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Security; using Umbraco.Core.Services; @@ -33,10 +22,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Binders; using Umbraco.Web.WebApi.Filters; -using umbraco; using Constants = Umbraco.Core.Constants; -using Examine; -using Newtonsoft.Json; namespace Umbraco.Web.Editors { @@ -96,16 +82,18 @@ namespace Umbraco.Web.Editors if (MembershipScenario == MembershipScenario.NativeUmbraco) { - long totalRecords; var members = Services.MemberService - .GetAll((pageNumber - 1), pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, memberTypeAlias, filter).ToArray(); + .GetAll((pageNumber - 1), pageSize, out var totalRecords, orderBy, orderDirection, orderBySystemField, memberTypeAlias, filter).ToArray(); if (totalRecords == 0) { return new PagedResult(0, 0, 0); } - var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize); - pagedResult.Items = members - .Select(Mapper.Map); + + var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize) + { + Items = members + .Select(x => AutoMapperExtensions.MapWithUmbracoContext(x, UmbracoContext)) + }; return pagedResult; } else @@ -133,10 +121,13 @@ namespace Umbraco.Web.Editors { return new PagedResult(0, 0, 0); } - var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize); - pagedResult.Items = members - .Cast() - .Select(Mapper.Map); + + var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize) + { + Items = members + .Cast() + .Select(Mapper.Map) + }; return pagedResult; } @@ -437,7 +428,7 @@ namespace Umbraco.Web.Editors var sensitiveProperties = contentItem.PersistedContent.ContentType .PropertyTypes.Where(x => contentItem.PersistedContent.ContentType.IsSensitiveProperty(x.Alias)) .ToList(); - + foreach (var sensitiveProperty in sensitiveProperties) { //if found, change the value of the contentItem model to the persisted value so it remains unchanged @@ -665,7 +656,7 @@ namespace Umbraco.Web.Editors contentItem.Email, "TEMP", //some membership provider's require something here even if q/a is disabled! "TEMP", //some membership provider's require something here even if q/a is disabled! - contentItem.IsApproved, + contentItem.IsApproved, contentItem.PersistedContent.Key, //custom membership provider, we'll link that based on the IMember unique id (GUID) out status); @@ -682,7 +673,7 @@ namespace Umbraco.Web.Editors contentItem.Email, "TEMP", //some membership provider's require something here even if q/a is disabled! "TEMP", //some membership provider's require something here even if q/a is disabled! - contentItem.IsApproved, + contentItem.IsApproved, newKey, out status); @@ -828,17 +819,17 @@ namespace Umbraco.Web.Editors var member = ((MemberService)Services.MemberService).ExportMember(key); var fileName = $"{member.Name}_{member.Email}.txt"; - - httpResponseMessage.Content = new ObjectContent(member, new JsonMediaTypeFormatter {Indent = true}); + + httpResponseMessage.Content = new ObjectContent(member, new JsonMediaTypeFormatter { Indent = true }); httpResponseMessage.Content.Headers.Add("x-filename", fileName); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName; httpResponseMessage.StatusCode = HttpStatusCode.OK; - + return httpResponseMessage; } } - + } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs index f317771333..4ed46de135 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs @@ -30,6 +30,11 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "editor", IsRequired = false)] public string Editor { get; set; } + /// + /// Flags the property to denote that it can contain sensitive data + /// + [DataMember(Name = "isSensitive", IsRequired = false)] + public bool IsSensitive { get; set; } /// /// Used internally during model mapping @@ -38,4 +43,4 @@ namespace Umbraco.Web.Models.ContentEditing internal PropertyEditor PropertyEditor { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs index 5c72879210..5b53f1ef8d 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -80,17 +80,21 @@ namespace Umbraco.Web.Models.Mapping //FROM IMember TO MemberBasic config.CreateMap() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) + .ForMember(display => display.Udi, + expression => + expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver())) .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(dto => dto.ContentTypeAlias, + expression => expression.MapFrom(content => content.ContentType.Alias)) .ForMember(dto => dto.Email, expression => expression.MapFrom(content => content.Email)) .ForMember(dto => dto.Username, expression => expression.MapFrom(content => content.Username)) .ForMember(dto => dto.Trashed, expression => expression.Ignore()) .ForMember(dto => dto.Published, expression => expression.Ignore()) .ForMember(dto => dto.Updater, expression => expression.Ignore()) .ForMember(dto => dto.Alias, expression => expression.Ignore()) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); + .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()) + .ForMember(dto => dto.Properties, expression => expression.ResolveUsing(new MemberBasicPropertiesResolver())); //FROM MembershipUser TO MemberBasic config.CreateMap() @@ -395,6 +399,8 @@ namespace Umbraco.Web.Models.Mapping //check permissions for viewing sensitive data if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false) { + //mark this property as sensitive + prop.IsSensitive = true; //mark this property as readonly so that it does not post any data prop.Readonly = true; //replace this editor with a sensitivevalue @@ -477,8 +483,51 @@ namespace Umbraco.Web.Models.Mapping return AutoMapperExtensions.MapWithUmbracoContext(member, context.GetUmbracoContext()); } } + + /// + /// A resolver to map properties to a collection of + /// + internal class MemberBasicPropertiesResolver : IValueResolver + { + public ResolutionResult Resolve(ResolutionResult source) + { + if (source.Value != null && (source.Value is IMember) == false) + throw new AutoMapperMappingException(string.Format("Value supplied is of type {0} but expected {1}.\nChange the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.", new object[] + { + source.Value.GetType(), + typeof (IMember) + })); + return source.New( + //perform the mapping with the current umbraco context + ResolveCore(source.Context.GetUmbracoContext(), (IMember)source.Value), typeof(IEnumerable)); + } + + private IEnumerable ResolveCore(UmbracoContext umbracoContext, IMember content) + { + var result = Mapper.Map, IEnumerable>( + // Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties. + content.Properties.OrderBy(prop => prop.PropertyType.SortOrder)) + .ToList(); + + var member = (IMember)content; + var memberType = member.ContentType; + + //now update the IsSensitive value + foreach (var prop in result) + { + //check if this property is flagged as sensitive + var isSensitiveProperty = memberType.IsSensitiveProperty(prop.Alias); + //check permissions for viewing sensitive data + if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false) + { + //mark this property as sensitive + prop.IsSensitive = true; + //clear the value + prop.Value = null; + } + } + return result; + } + } } } - - - diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs index 7d54650c96..82d4855f32 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Globalization; using System.Linq; using AutoMapper; using Umbraco.Core; @@ -9,7 +7,6 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; -using umbraco; namespace Umbraco.Web.Models.Mapping { @@ -53,7 +50,7 @@ namespace Umbraco.Web.Models.Mapping //perform the mapping with the current umbraco context ResolveCore(source.Context.GetUmbracoContext(), (TSource)source.Value), typeof(List>)); } - + /// /// Adds the container (listview) tab to the document /// @@ -275,7 +272,7 @@ namespace Umbraco.Web.Models.Mapping //now add the user props contentProps.AddRange(currProps); - + //re-assign genericProps.Properties = contentProps; @@ -308,6 +305,6 @@ namespace Umbraco.Web.Models.Mapping return result; } - + } }