Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into pr/2507

This commit is contained in:
Sebastiaan Janssen
2018-03-15 09:38:44 +01:00
20 changed files with 327 additions and 126 deletions
+24
View File
@@ -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:
+1 -1
View File
@@ -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
@@ -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
};
}
}
}
/// <summary>
/// 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
}
}
}
}
@@ -280,7 +280,6 @@
.umb-grid .umb-control {
position: relative;
display: block;
overflow: hidden;
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
@@ -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{
@@ -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
// -------------------------
@@ -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);
}
};
}
);
@@ -16,24 +16,25 @@
<div class="umb-control-group">
<select ng-model="color" class="input-block-level">
<option value="">
<localize key="colors_black">Black</localize>
</option>
<option value="color-green">
<localize key="colors_green">Green</localize>
</option>
<option value="color-yellow">
<localize key="colors_yellow">Yellow</localize>
</option>
<option value="color-orange">
<localize key="colors_orange">Orange</localize>
</option>
<option value="color-blue">
<localize key="colors_blue">Blue</localize>
</option>
<option value="color-red">
<localize key="colors_red">Red</localize>
</option>
<option value=""><localize key="colors_black">Black</localize></option>
<option value="color-blue-grey"><localize key="colors_bluegrey">Blue Grey</localize></option>
<option value="color-grey"><localize key="colors_grey">Grey</localize></option>
<option value="color-brown"><localize key="colors_brown">Brown</localize></option>
<option value="color-blue"><localize key="colors_blue">Blue</localize></option>
<option value="color-light-blue"><localize key="colors_lightblue">Light Blue</localize></option>
<option value="color-cyan"><localize key="colors_cyan">Cyan</localize></option>
<option value="color-green"><localize key="colors_green">Green</localize></option>
<option value="color-light-green"><localize key="colors_lightgreen">Light Green</localize></option>
<option value="color-yellow"><localize key="colors_yellow">Yellow</localize></option>
<option value="color-lime"><localize key="colors_lime">Lime</localize></option>
<option value="color-amber"><localize key="colors_amber">Amber</localize></option>
<option value="color-orange"><localize key="colors_orange">Orange</localize></option>
<option value="color-deep-orange"><localize key="colors_deeporange">Deep Orange</localize></option>
<option value="color-red"><localize key="colors_red">Red</localize></option>
<option value="color-pink"><localize key="colors_pink">Pink</localize></option>
<option value="color-purple"><localize key="colors_purple">Purple</localize></option>
<option value="color-deep-purple"><localize key="colors_deeppurple">Deep Purple</localize></option>
<option value="color-indigo"><localize key="colors_indigo">Indigo</localize></option>
</select>
</div>
@@ -47,7 +47,17 @@
</a>
</div>
<div class="umb-table-cell" ng-repeat="column in itemProperties">
<span title="{{column.header}}: {{item[column.alias]}}">{{item[column.alias]}}</span>
<span title="{{column.header}}: {{item[column.alias]}}">
<div ng-if="!column.isSensitive">
{{item[column.alias]}}
</div>
<em ng-show="column.isSensitive" class="muted">
<localize key="content_isSensitiveValue_short"></localize>
</em>
</span>
</div>
</div>
</div>
@@ -20,7 +20,7 @@
</umb-node-preview>
</div>
<a ng-show="model.config.multiPicker === true || renderModel.length === 0"
<a ng-show="model.config.multiPicker === true && renderModel.length < model.config.maxNumber || renderModel.length === 0 || !model.config.maxNumber"
class="umb-node-preview-add"
href=""
ng-click="openContentPicker()"
@@ -28,6 +28,39 @@
<localize key="general_add">Add</localize>
</a>
<div class="umb-contentpicker__min-max-help">
<!-- Both min and max items -->
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber !== model.config.maxNumber">
<span ng-if="renderModel.length < model.config.maxNumber">Add between {{model.config.minNumber}} and {{model.config.maxNumber}} items</span>
<span ng-if="renderModel.length > model.config.maxNumber">
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
</span>
</span>
<!-- Equal min and max -->
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber === model.config.maxNumber">
<span ng-if="renderModel.length < model.config.maxNumber">Add {{model.config.minNumber - renderModel.length}} item(s)</span>
<span ng-if="renderModel.length > model.config.maxNumber">
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
</span>
</span>
<!-- Only max -->
<span ng-if="!model.config.minNumber && model.config.maxNumber">
<span ng-if="renderModel.length < model.config.maxNumber">Add up to {{model.config.maxNumber}} items</span>
<span ng-if="renderModel.length > model.config.maxNumber">
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected">items selected</localize>
</span>
</span>
<!-- Only min -->
<span ng-if="model.config.minNumber && !model.config.maxNumber && renderModel.length < model.config.minNumber">
Add at least {{model.config.minNumber}} item(s)
</span>
</div>
<!--These are here because we need ng-form fields to validate against-->
<input type="hidden" name="minCount" ng-model="renderModel" />
<input type="hidden" name="maxCount" ng-model="renderModel" />
@@ -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();
}
@@ -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();
}
});
@@ -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);
@@ -661,7 +661,7 @@
<area alias="graphicheadline">
<key alias="backgroundcolor">Baggrundsfarve</key>
<key alias="bold">Fed</key>
<key alias="color">Tekst farve</key>
<key alias="color">Tekstfarve</key>
<key alias="font">Skrifttype</key>
<key alias="text">Tekst</key>
</area>
@@ -1259,9 +1259,9 @@ Mange hilsner fra Umbraco robotten
<key alias="styles">Typografi</key>
<key alias="stylesDetails">Vælg, hvilke typografiværdier, en redaktør kan ændre</key>
<key alias="stylesDetails">Vælg hvilke typografiværdier en redaktør kan ændre</key>
<key alias="settingDialogDetails">Indstillinger gemmes kun, hvis den indtaste json-konfiguration er gyldig</key>
<key alias="settingDialogDetails">Indstillinger gemmes kun, hvis den indtastede json-konfiguration er gyldig</key>
<key alias="allowAllEditors">Tillad alle editorer</key>
<key alias="allowAllRowConfigurations">Tillad alle rækkekonfigurationer</key>
@@ -1272,7 +1272,7 @@ Mange hilsner fra Umbraco robotten
<key alias="areAdded">er tilføjet</key>
<key alias="maxItems">Maksimalt emner</key>
<key alias="maxItemsDescription">Efterlad blank eller sat til 0 ubegrænset for</key>
<key alias="maxItemsDescription">Efterlad blank eller sæt til 0 for ubegrænset</key>
</area>
<area alias="contentTypeEditor">
@@ -1282,7 +1282,7 @@ Mange hilsner fra Umbraco robotten
<key alias="addNewTab">Tilføj ny fane</key>
<key alias="addAnotherTab">Tilføj endnu en fane</key>
<key alias="inheritedFrom">Nedarvet fra</key>
<key alias="addProperty">Tilføj property</key>
<key alias="addProperty">Tilføj egenskab</key>
<key alias="requiredLabel">Påkrævet label</key>
<key alias="enableListViewHeading">Aktiver listevisning</key>
@@ -1473,7 +1473,6 @@ Mange hilsner fra Umbraco robotten
<key alias="noLogin">har endnu ikke logget ind</key>
<key alias="oldPassword">Gammelt kodeord</key>
<key alias="password">Adgangskode</key>
<key alias="removePhoto">Fjern billede</key>
<key alias="resetPassword">Nulstil kodeord</key>
<key alias="passwordChanged">Dit kodeord er blevet ændret!</key>
<key alias="passwordConfirm">Bekræft venligst dit nye kodeord</key>
@@ -229,6 +229,7 @@
<key alias="removeTextBox">Remove this text box</key>
<key alias="contentRoot">Content root</key>
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
<key alias="isSensitiveValue_short">This value is hidden.</key>
</area>
<area alias="blueprints">
<key alias="createBlueprintFrom">Create a new Content Template from '%0%'</key>
@@ -662,7 +663,20 @@
<key alias="yellow">Yellow</key>
<key alias="orange">Orange</key>
<key alias="blue">Blue</key>
<key alias="bluegrey">Blue Grey</key>
<key alias="grey">Grey</key>
<key alias="brown">Brown</key>
<key alias="lightblue">Light Blue</key>
<key alias="cyan">Cyan</key>
<key alias="lightgreen">Light Green</key>
<key alias="lime">Lime</key>
<key alias="amber">Amber</key>
<key alias="deeporange">Deep Orange</key>
<key alias="red">Red</key>
<key alias="pink">Pink</key>
<key alias="purple">Purple</key>
<key alias="deeppurple">Deep Purple</key>
<key alias="indigo">Indigo</key>
</area>
<area alias="shortcuts">
@@ -229,6 +229,7 @@
<key alias="removeTextBox">Remove this text box</key>
<key alias="contentRoot">Content root</key>
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
<key alias="isSensitiveValue_short">This value is hidden.</key>
</area>
<area alias="blueprints">
<key alias="createBlueprintFrom">Create a new Content Template from '%0%'</key>
@@ -660,7 +661,20 @@
<key alias="yellow">Yellow</key>
<key alias="orange">Orange</key>
<key alias="blue">Blue</key>
<key alias="bluegrey">Blue Grey</key>
<key alias="grey">Grey</key>
<key alias="brown">Brown</key>
<key alias="lightblue">Light Blue</key>
<key alias="cyan">Cyan</key>
<key alias="lightgreen">Light Green</key>
<key alias="lime">Lime</key>
<key alias="amber">Amber</key>
<key alias="deeporange">Deep Orange</key>
<key alias="red">Red</key>
<key alias="pink">Pink</key>
<key alias="purple">Purple</key>
<key alias="deeppurple">Deep Purple</key>
<key alias="indigo">Indigo</key>
</area>
<area alias="shortcuts">
<key alias="addTab">Add tab</key>
+21 -30
View File
@@ -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<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize);
pagedResult.Items = members
.Select(Mapper.Map<IMember, MemberBasic>);
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Select(x => AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberBasic>(x, UmbracoContext))
};
return pagedResult;
}
else
@@ -133,10 +121,13 @@ namespace Umbraco.Web.Editors
{
return new PagedResult<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize);
pagedResult.Items = members
.Cast<MembershipUser>()
.Select(Mapper.Map<MembershipUser, MemberBasic>);
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Cast<MembershipUser>()
.Select(Mapper.Map<MembershipUser, MemberBasic>)
};
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<MemberExportModel>(member, new JsonMediaTypeFormatter {Indent = true});
httpResponseMessage.Content = new ObjectContent<MemberExportModel>(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;
}
}
}
@@ -30,6 +30,11 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "editor", IsRequired = false)]
public string Editor { get; set; }
/// <summary>
/// Flags the property to denote that it can contain sensitive data
/// </summary>
[DataMember(Name = "isSensitive", IsRequired = false)]
public bool IsSensitive { get; set; }
/// <summary>
/// Used internally during model mapping
@@ -38,4 +43,4 @@ namespace Umbraco.Web.Models.ContentEditing
internal PropertyEditor PropertyEditor { get; set; }
}
}
}
@@ -80,17 +80,21 @@ namespace Umbraco.Web.Models.Mapping
//FROM IMember TO MemberBasic
config.CreateMap<IMember, MemberBasic>()
.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<IMember>()))
.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<MembershipUser, MemberBasic>()
@@ -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<IMember, MemberDisplay>(member, context.GetUmbracoContext());
}
}
/// <summary>
/// A resolver to map <see cref="IMember"/> properties to a collection of <see cref="ContentPropertyBasic"/>
/// </summary>
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<ContentPropertyDisplay>));
}
private IEnumerable<ContentPropertyBasic> ResolveCore(UmbracoContext umbracoContext, IMember content)
{
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyBasic>>(
// 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;
}
}
}
}
@@ -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<Tab<ContentPropertyDisplay>>));
}
/// <summary>
/// Adds the container (listview) tab to the document
/// </summary>
@@ -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;
}
}
}