-
-
-
+
+
-
-
-
- Add
-
-
+
+
+
+
+ {{ invalidEntry.name }}
+ {{ invalidEntry.errorText }}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less
index 37e7a4ee76..7f3af3aaa5 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less
@@ -18,4 +18,41 @@ umb-media-picker3-property-editor[readonly] {
border-color: @inputReadonlyBorderColor;
}
}
-}
\ No newline at end of file
+}
+
+.umb-mediapicker3 {
+ .dropzone {
+ position: relative;
+
+ .drop-overlay {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+ padding: 12px;
+ background-color: rgba(27,38,79,.2);
+ z-index: 1;
+ backdrop-filter: blur(0px);
+ opacity: 0;
+ pointer-events: none;
+ transition: backdrop-filter 200ms ease-in-out, opacity 200ms ease-in-out;
+ margin: -1px;
+ }
+
+ &.drag-over {
+ .drop-overlay {
+ backdrop-filter: blur(2px);
+ opacity: 1;
+ pointer-events: all;
+ border: 2px solid @blueExtraDark;
+ }
+ }
+ };
+}
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js
index 25ca237bb9..e052e7c1c6 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js
@@ -17,7 +17,8 @@
controller: MediaPicker3Controller,
controllerAs: "vm",
bindings: {
- model: "="
+ model: "=",
+ node: "="
},
require: {
propertyForm: "^form",
@@ -28,7 +29,10 @@
}
});
- function MediaPicker3Controller($scope, editorService, clipboardService, localizationService, overlayService, userService, entityResource, $attrs) {
+ function MediaPicker3Controller($scope, editorService, clipboardService, localizationService, overlayService, userService, entityResource, $attrs, umbRequestHelper, $injector, uploadTracker) {
+
+ const mediaUploader = $injector.instantiate(Utilities.MediaUploader);
+ let uploadInProgress = false;
var unsubscribe = [];
@@ -47,10 +51,15 @@
vm.allowRemoveMedia = true;
vm.allowEditMedia = true;
+ vm.handleFiles = handleFiles;
+
+ vm.invalidEntries = [];
+
vm.addMediaAt = addMediaAt;
vm.editMedia = editMedia;
vm.removeMedia = removeMedia;
vm.copyMedia = copyMedia;
+ vm.allowDir = true;
vm.labels = {};
@@ -70,7 +79,6 @@
});
vm.$onInit = function() {
-
vm.validationLimit = vm.model.config.validationLimit || {};
// If single-mode we only allow 1 item as the maximum:
if(vm.model.config.multiple === false) {
@@ -80,6 +88,17 @@
vm.singleMode = vm.validationLimit.max === 1;
vm.allowedTypes = vm.model.config.filter ? vm.model.config.filter.split(",") : null;
+ const uploaderOptions = {
+ uploadURL: umbRequestHelper.getApiUrl("mediaPickerThreeBaseUrl", "uploadMedia"),
+ allowedMediaTypeAliases: vm.allowedTypes
+ };
+
+ unsubscribe.push(mediaUploader.on('mediaEntryAccepted', _handleMediaEntryAccepted));
+ unsubscribe.push(mediaUploader.on('mediaEntryRejected', _handleMediaEntryRejected));
+ unsubscribe.push(mediaUploader.on('queueStarted', _handleMediaQueueStarted));
+ unsubscribe.push(mediaUploader.on('uploadSuccess', _handleMediaUploadSuccess));
+ unsubscribe.push(mediaUploader.on('queueCompleted', _handleMediaQueueCompleted));
+
copyAllMediasAction = {
labelKey: "clipboard_labelForCopyAllEntries",
labelTokens: [vm.model.label],
@@ -135,11 +154,51 @@
vm.allowEdit = hasAccessToMedia;
vm.allowAdd = hasAccessToMedia;
- vm.loading = false;
+ mediaUploader.init(uploaderOptions).then(() => {
+ vm.loading = false;
+ });
});
-
};
+ function handleFiles (files, invalidFiles) {
+ if (vm.readonly) return;
+ const allFiles = [...files, ...invalidFiles];
+ mediaUploader.requestUpload(allFiles);
+ };
+
+ function _handleMediaEntryAccepted (event, data) {
+ vm.model.value.push(data.mediaEntry);
+ setDirty();
+ }
+
+ function _handleMediaEntryRejected (event, data) {
+ // we need to make sure the media entry hasn't been accepted earlier in process
+ const index = vm.model.value.findIndex(mediaEntry => mediaEntry.key === data.mediaEntry.key);
+ if (index !== -1) {
+ vm.model.value.splice(index, 1);
+ }
+ vm.invalidEntries.push(data.mediaEntry);
+ setDirty();
+ }
+
+ function _handleMediaUploadSuccess (event, data) {
+ const mediaEntry = vm.model.value.find(mediaEntry => mediaEntry.key === data.mediaEntry.key);
+ if (!mediaEntry) return;
+
+ mediaEntry.tmpLocation = data.tmpLocation;
+ updateMediaEntryData(mediaEntry);
+ }
+
+ function _handleMediaQueueStarted () {
+ uploadInProgress = true;
+ uploadTracker.uploadStarted(vm.node.key);
+ }
+
+ function _handleMediaQueueCompleted () {
+ uploadInProgress = false;
+ uploadTracker.uploadEnded(vm.node.key);
+ }
+
function onServerValueChanged(newVal, oldVal) {
if(newVal === null || !Array.isArray(newVal)) {
newVal = [];
@@ -430,7 +489,7 @@
vm.sortableOptions = {
cursor: "grabbing",
- handle: "umb-media-card",
+ handle: "umb-media-card, .umb-media-card",
cancel: "input,textarea,select,option",
classes: ".umb-media-card--dragging",
distance: 5,
@@ -469,6 +528,10 @@
for (const subscription of unsubscribe) {
subscription();
}
+
+ if (uploadInProgress) {
+ uploadTracker.uploadEnded(vm.node.key);
+ }
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js
index 8efbed00a4..dfdc9aa779 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js
@@ -97,13 +97,13 @@ function multiUrlPickerController($scope, localizationService, entityResource, i
setDirty();
};
- $scope.clear = function ($index) {
+ $scope.clear = function () {
$scope.renderModel = [];
setDirty();
};
- $scope.openLinkPicker = function (link, $index) {
+ $scope.openLinkPicker = function (link) {
if (!$scope.allowAdd || !$scope.allowEdit) return;
var target = link ? {
@@ -153,7 +153,7 @@ function multiUrlPickerController($scope, localizationService, entityResource, i
link.trashed = data.trashed;
if (link.trashed) {
- item.url = vm.labels.general_recycleBin;
+ link.url = vm.labels.general_recycleBin;
}
});
} else {
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
index 7fcf5cc365..274fd1f15d 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
@@ -79,7 +79,7 @@
});
_.each(model.config.contentTypes, function (contentType) {
- contentType.nameExp = !!contentType.nameTemplate
+ contentType.nameExp = contentType.nameTemplate
? $interpolate(contentType.nameTemplate)
: undefined;
});
@@ -626,7 +626,7 @@
// Enforce min items if we only have one scaffold type
var modelWasChanged = false;
if (vm.nodes.length < vm.minItems && vm.scaffolds.length === 1) {
- for (var i = vm.nodes.length; i < model.config.minItems; i++) {
+ for (var ii = vm.nodes.length; ii < model.config.minItems; ii++) {
addNode(vm.scaffolds[0].contentTypeAlias);
}
modelWasChanged = true;
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js
index 4e90dd063b..454a4e5615 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js
@@ -112,10 +112,6 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
icon.name = "icon-settings-alt";
icon.isCustom = true;
break;
- case "umbmacro":
- icon.name = "icon-settings-alt";
- icon.isCustom = true;
- break;
default:
icon.name = alias;
icon.isCustom = false;
@@ -124,7 +120,7 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
return icon;
}
- var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
+ var unsubscribe = $scope.$on("formSubmitting", function () {
var commands = _.where($scope.tinyMceConfig.commands, {selected: true});
$scope.model.value.toolbar = _.pluck(commands, "alias");
diff --git a/src/Umbraco.Web.UI.Client/src/views/relationTypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationTypes/edit.controller.js
index 1f0c054257..c061bacf26 100644
--- a/src/Umbraco.Web.UI.Client/src/views/relationTypes/edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/relationTypes/edit.controller.js
@@ -6,7 +6,7 @@
* @description
* The controller for editing relation types.
*/
-function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, entityResource, formHelper, contentEditingHelper, localizationService, eventsService) {
+function RelationTypeEditController($scope, $routeParams, relationTypeResource, editorState, navigationService, dateHelper, userService, notificationsService, formHelper, contentEditingHelper, localizationService, eventsService) {
var vm = this;
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
index 1e14cf1e87..1b6b000e28 100644
--- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
@@ -361,6 +361,7 @@
vm.close = close;
function openInsertOverlay() {
+ var code;
var insertOverlay = {
allowedTypes: {
macro: true,
@@ -375,11 +376,11 @@
insert(macroObject.syntax);
break;
case "dictionary":
- var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name);
+ code = templateHelper.getInsertDictionarySnippet(model.insert.node.name);
insert(code);
break;
case "partial":
- var code = templateHelper.getInsertPartialSnippet(model.insert.node.parentId, model.insert.node.name);
+ code = templateHelper.getInsertPartialSnippet(model.insert.node.parentId, model.insert.node.name);
insert(code);
break;
case "umbracoField":
@@ -388,7 +389,7 @@
}
editorService.close();
},
- close: function (oldModel) {
+ close: function () {
// close the dialog
editorService.close();
// focus editor
@@ -452,7 +453,7 @@
insert(code);
editorService.close();
},
- close: function (model) {
+ close: function () {
// close dialog
editorService.close();
// focus editor
@@ -488,7 +489,7 @@
insert(code);
editorService.close();
},
- close: function (model) {
+ close: function () {
// close dialog
editorService.close();
// focus editor
@@ -518,29 +519,30 @@
function openSectionsOverlay() {
+ var code;
var templateSections = {
isMaster: vm.template.isMasterTemplate,
submit: function (model) {
if (model.insertType === 'renderBody') {
- var code = templateHelper.getRenderBodySnippet();
+ code = templateHelper.getRenderBodySnippet();
insert(code);
}
if (model.insertType === 'renderSection') {
- var code = templateHelper.getRenderSectionSnippet(model.renderSectionName, model.mandatoryRenderSection);
+ code = templateHelper.getRenderSectionSnippet(model.renderSectionName, model.mandatoryRenderSection);
insert(code);
}
if (model.insertType === 'addSection') {
- var code = templateHelper.getAddSectionSnippet(model.sectionName);
+ code = templateHelper.getAddSectionSnippet(model.sectionName);
wrap(code);
}
editorService.close();
},
- close: function (model) {
+ close: function () {
editorService.close();
vm.editor.focus();
}
diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js
index 89101d2f1d..6619754315 100644
--- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js
+++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js
@@ -29,7 +29,6 @@ module.exports = function (config) {
'lib/umbraco/Extensions.js',
'node_modules/lazyload-js/LazyLoad.min.js',
'node_modules/angular-dynamic-locale/dist/tmhDynamicLocale.min.js',
- 'node_modules/sortablejs/Sortable.min.js',
//app bootstrap and loader
'test/config/app.unit.js',
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index e1a0831efd..45d32a65ee 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -28,15 +28,6 @@
false
-
-
- appsettings.json
-
-
- appsettings-schema.json
-
-
-
diff --git a/src/Umbraco.Web.UI/appsettings.Development.template.json b/src/Umbraco.Web.UI/appsettings.Development.template.json
index b2bde5bf2d..b02fdd604a 100644
--- a/src/Umbraco.Web.UI/appsettings.Development.template.json
+++ b/src/Umbraco.Web.UI/appsettings.Development.template.json
@@ -1,5 +1,5 @@
{
- "$schema" : "./appsettings-schema.json",
+ "$schema": "appsettings-schema.json",
"Serilog": {
"MinimumLevel": {
"Default": "Information",
diff --git a/src/Umbraco.Web.UI/appsettings.template.json b/src/Umbraco.Web.UI/appsettings.template.json
index b7aa07acf8..c0d6784ac2 100644
--- a/src/Umbraco.Web.UI/appsettings.template.json
+++ b/src/Umbraco.Web.UI/appsettings.template.json
@@ -1,5 +1,5 @@
{
- "$schema": "./appsettings-schema.json",
+ "$schema": "appsettings-schema.json",
"ConnectionStrings": {
"umbracoDbDSN": ""
},
diff --git a/templates/UmbracoProject/.gitignore b/templates/UmbracoProject/.gitignore
index 088126cc3a..1b6f65e6da 100644
--- a/templates/UmbracoProject/.gitignore
+++ b/templates/UmbracoProject/.gitignore
@@ -459,8 +459,9 @@ $RECYCLE.BIN/
## Umbraco CMS
##
-# JSON schema file for appsettings.json
+# JSON schema files for appsettings.json
appsettings-schema.json
+appsettings-schema.*.json
# Packages created from the backoffice (package.xml/package.zip)
/umbraco/Data/CreatedPackages/
diff --git a/templates/UmbracoProject/UmbracoProject.csproj b/templates/UmbracoProject/UmbracoProject.csproj
index a71d367a7a..00f7c4b6ff 100644
--- a/templates/UmbracoProject/UmbracoProject.csproj
+++ b/templates/UmbracoProject/UmbracoProject.csproj
@@ -26,15 +26,6 @@
false
false
-
-
-
- appsettings.json
-
-
- appsettings-schema.json
-
-
diff --git a/templates/UmbracoProject/appsettings.Development.json b/templates/UmbracoProject/appsettings.Development.json
index d789f6cd32..17b9f86361 100644
--- a/templates/UmbracoProject/appsettings.Development.json
+++ b/templates/UmbracoProject/appsettings.Development.json
@@ -1,5 +1,5 @@
{
- "$schema": "./appsettings-schema.json",
+ "$schema": "appsettings-schema.json",
"Serilog": {
"MinimumLevel": {
"Default": "Information"
diff --git a/templates/UmbracoProject/appsettings.json b/templates/UmbracoProject/appsettings.json
index 85f6c15dc7..f1e53a9e48 100644
--- a/templates/UmbracoProject/appsettings.json
+++ b/templates/UmbracoProject/appsettings.json
@@ -1,5 +1,5 @@
{
- "$schema": "./appsettings-schema.json",
+ "$schema": "appsettings-schema.json",
"Serilog": {
"MinimumLevel": {
"Default": "Information",
diff --git a/tools/Umbraco.JsonSchema/Options.cs b/tools/Umbraco.JsonSchema/Options.cs
new file mode 100644
index 0000000000..e2e8793db4
--- /dev/null
+++ b/tools/Umbraco.JsonSchema/Options.cs
@@ -0,0 +1,7 @@
+using CommandLine;
+
+internal class Options
+{
+ [Option("outputFile", Default = "appsettings-schema.Umbraco.Cms.json", HelpText = "Output file to save the generated JSON schema for Umbraco CMS.")]
+ public string OutputFile { get; set; } = null!;
+}
diff --git a/tools/Umbraco.JsonSchema/Program.cs b/tools/Umbraco.JsonSchema/Program.cs
new file mode 100644
index 0000000000..adf17c5c8c
--- /dev/null
+++ b/tools/Umbraco.JsonSchema/Program.cs
@@ -0,0 +1,14 @@
+using CommandLine;
+using Umbraco.Cms.Core.Configuration.Models;
+
+await Parser.Default.ParseArguments(args).WithParsedAsync(async options =>
+{
+ // Generate CMS schema
+ var jsonSchemaGenerator = new UmbracoJsonSchemaGenerator();
+ var jsonSchema = jsonSchemaGenerator.Generate(typeof(UmbracoCmsSchema));
+
+ // TODO: When the UmbracoPath setter is removed from GlobalSettings (scheduled for V12), remove this line as well
+ jsonSchema.Definitions[nameof(GlobalSettings)]?.Properties?.Remove(nameof(GlobalSettings.UmbracoPath));
+
+ await File.WriteAllTextAsync(options.OutputFile, jsonSchema.ToJson());
+});
diff --git a/src/JsonSchema/JsonSchema.csproj b/tools/Umbraco.JsonSchema/Umbraco.JsonSchema.csproj
similarity index 62%
rename from src/JsonSchema/JsonSchema.csproj
rename to tools/Umbraco.JsonSchema/Umbraco.JsonSchema.csproj
index 36463de3e0..32da8f798a 100644
--- a/src/JsonSchema/JsonSchema.csproj
+++ b/tools/Umbraco.JsonSchema/Umbraco.JsonSchema.csproj
@@ -8,14 +8,9 @@
-
-
-
-
-
-
+
diff --git a/tools/Umbraco.JsonSchema/UmbracoCmsSchema.cs b/tools/Umbraco.JsonSchema/UmbracoCmsSchema.cs
new file mode 100644
index 0000000000..86e64d2aa5
--- /dev/null
+++ b/tools/Umbraco.JsonSchema/UmbracoCmsSchema.cs
@@ -0,0 +1,81 @@
+using Umbraco.Cms.Core.Configuration;
+using Umbraco.Cms.Core.Configuration.Models;
+
+internal class UmbracoCmsSchema
+{
+ public UmbracoDefinition Umbraco { get; set; } = null!;
+
+ ///
+ /// Configuration container for all Umbraco products.
+ ///
+ public class UmbracoDefinition
+ {
+ public UmbracoCmsDefinition CMS { get; set; } = null!;
+ }
+
+ ///
+ /// Configuration of Umbraco CMS.
+ ///
+ public class UmbracoCmsDefinition
+ {
+ public ContentSettings Content { get; set; } = null!;
+
+ public CoreDebugSettings Debug { get; set; } = null!;
+
+ public ExceptionFilterSettings ExceptionFilter { get; set; } = null!;
+
+ public ModelsBuilderSettings ModelsBuilder { get; set; } = null!;
+
+ public GlobalSettings Global { get; set; } = null!;
+
+ public HealthChecksSettings HealthChecks { get; set; } = null!;
+
+ public HostingSettings Hosting { get; set; } = null!;
+
+ public ImagingSettings Imaging { get; set; } = null!;
+
+ public IndexCreatorSettings Examine { get; set; } = null!;
+
+ public KeepAliveSettings KeepAlive { get; set; } = null!;
+
+ public LoggingSettings Logging { get; set; } = null!;
+
+ public NuCacheSettings NuCache { get; set; } = null!;
+
+ public RequestHandlerSettings RequestHandler { get; set; } = null!;
+
+ public RuntimeSettings Runtime { get; set; } = null!;
+
+ public SecuritySettings Security { get; set; } = null!;
+
+ public TourSettings Tours { get; set; } = null!;
+
+ public TypeFinderSettings TypeFinder { get; set; } = null!;
+
+ public WebRoutingSettings WebRouting { get; set; } = null!;
+
+ public UmbracoPluginSettings Plugins { get; set; } = null!;
+
+ public UnattendedSettings Unattended { get; set; } = null!;
+
+ public RichTextEditorSettings RichTextEditor { get; set; } = null!;
+
+ public RuntimeMinificationSettings RuntimeMinification { get; set; } = null!;
+
+ public BasicAuthSettings BasicAuth { get; set; } = null!;
+
+ public PackageMigrationSettings PackageMigration { get; set; } = null!;
+
+ public LegacyPasswordMigrationSettings LegacyPasswordMigration { get; set; } = null!;
+
+ public ContentDashboardSettings ContentDashboard { get; set; } = null!;
+
+ public HelpPageSettings HelpPage { get; set; } = null!;
+
+ public InstallDefaultDataSettings DefaultDataCreation { get; set; } = null!;
+
+ public DataTypesSettings DataTypes { get; set; } = null!;
+
+ public MarketplaceSettings Marketplace { get; set; } = null!;
+ }
+}
diff --git a/tools/Umbraco.JsonSchema/UmbracoJsonSchemaGenerator.cs b/tools/Umbraco.JsonSchema/UmbracoJsonSchemaGenerator.cs
new file mode 100644
index 0000000000..f1a55479e3
--- /dev/null
+++ b/tools/Umbraco.JsonSchema/UmbracoJsonSchemaGenerator.cs
@@ -0,0 +1,35 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Serialization;
+using NJsonSchema.Generation;
+
+///
+public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UmbracoJsonSchemaGenerator()
+ : base(new JsonSchemaGeneratorSettings()
+ {
+ AlwaysAllowAdditionalObjectProperties = true,
+ DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull,
+ FlattenInheritanceHierarchy = true,
+ IgnoreObsoleteProperties = true,
+ SerializerSettings = new JsonSerializerSettings()
+ {
+ ContractResolver = new WritablePropertiesOnlyResolver()
+ }
+ })
+ {
+ Settings.SerializerSettings.Converters.Add(new StringEnumConverter());
+ }
+
+ ///
+ private class WritablePropertiesOnlyResolver : DefaultContractResolver
+ {
+ ///
+ protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
+ => base.CreateProperties(type, memberSerialization).Where(p => p.Writable).ToList();
+ }
+}
diff --git a/umbraco.sln b/umbraco.sln
index a98ac5cc0d..f0122909d6 100644
--- a/umbraco.sln
+++ b/umbraco.sln
@@ -81,7 +81,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Tests.UnitTests", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.Common", "src\Umbraco.Web.Common\Umbraco.Web.Common.csproj", "{79E4293D-C92C-4649-AEC8-F1EFD95BDEB1}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonSchema", "src\JsonSchema\JsonSchema.csproj", "{2A5027D9-F71D-4957-929E-F7A56AA1B95A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.JsonSchema", "tools\Umbraco.JsonSchema\Umbraco.JsonSchema.csproj", "{2A5027D9-F71D-4957-929E-F7A56AA1B95A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.Sqlite", "src\Umbraco.Cms.Persistence.Sqlite\Umbraco.Cms.Persistence.Sqlite.csproj", "{32F6A309-EC1E-4CDB-BA80-C804CF680BEE}"
EndProject
@@ -156,6 +156,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "styles", "styles", "{EA628A
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Imaging.ImageSharp", "src\Umbraco.Cms.Imaging.ImageSharp\Umbraco.Cms.Imaging.ImageSharp.csproj", "{C280181E-597B-4AA5-82E7-D7017E928749}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{05878304-40EB-4F84-B40B-91BDB70DE094}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -325,6 +327,7 @@ Global
{D6319409-777A-4BD0-93ED-B2DFD805B32C} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
{A499779C-1B3B-48A8-B551-458E582E6E96} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
{9102ABDF-E537-4E46-B525-C9ED4833EED0} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
+ {2A5027D9-F71D-4957-929E-F7A56AA1B95A} = {05878304-40EB-4F84-B40B-91BDB70DE094}
{05C1D0C8-C592-468F-AF8F-A299B9B3A903} = {6D72A60B-0542-4AA9-A493-DD4179E838A1}
{0946531B-F06D-415B-A4E3-6CBFF5DB1C12} = {995D9EFA-8BB1-4333-80AD-C525A06FD984}
{CBCE0A1E-BF29-49A6-9581-EAB3587D823A} = {995D9EFA-8BB1-4333-80AD-C525A06FD984}