Merge remote-tracking branch 'origin/v8/dev' into v9/dev

# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs
#	src/Umbraco.Core/Extensions/PublishedContentExtensions.cs
#	src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs
#	src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs
#	src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs
#	src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml
#	src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs
This commit is contained in:
Bjarke Berg
2021-05-31 14:39:54 +02:00
21 changed files with 134 additions and 110 deletions

View File

@@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Serialization;
@@ -62,6 +63,11 @@ namespace Umbraco.Cms.Core.Cache
foreach (var payload in payloads)
{
_idKeyMap.ClearCache(payload.Id);
if (dataTypeCache.Success)
{
dataTypeCache.Result.Clear(RepositoryCacheKeys.GetKey<IDataType, int>(payload.Id));
}
}
// TODO: not sure I like these?

View File

@@ -262,7 +262,7 @@ namespace Umbraco.Extensions
// else... if we have a property, at least let the converter return its own
// vision of 'no value' (could be an empty enumerable) - otherwise, default
return property == null ? default : property.Value<T>(publishedValueFallback, culture, segment, fallback, defaultValue);
return property == null ? default : property.Value<T>(publishedValueFallback, culture, segment);
}
#endregion

View File

@@ -32,16 +32,9 @@ namespace Umbraco.Extensions
// we have a value
// try to cast or convert it
var value = property.GetValue(culture, segment);
if (value is T valueAsT)
{
return valueAsT;
}
if (value is T valueAsT) return valueAsT;
var valueConverted = value.TryConvertTo<T>();
if (valueConverted)
{
return valueConverted.Result;
}
if (valueConverted) return valueConverted.Result;
// cannot cast nor convert the value, nothing we can return but 'default'
// note: we don't want to fallback in that case - would make little sense
@@ -50,28 +43,14 @@ namespace Umbraco.Extensions
// we don't have a value, try fallback
if (publishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var fallbackValue))
{
return fallbackValue;
}
// we don't have a value - neither direct nor fallback
// give a chance to the converter to return something (eg empty enumerable)
var noValue = property.GetValue(culture, segment);
if (noValue == null)
{
return default;
}
if (noValue is T noValueAsT)
{
return noValueAsT;
}
if (noValue is T noValueAsT) return noValueAsT;
var noValueConverted = noValue.TryConvertTo<T>();
if (noValueConverted)
{
return noValueConverted.Result;
}
if (noValueConverted) return noValueConverted.Result;
// cannot cast noValue nor convert it, nothing we can return but 'default'
return default;

View File

@@ -5,6 +5,9 @@
/// </summary>
public class MediaPickerConfiguration : IIgnoreUserStartNodesConfig
{
[ConfigurationField("notice", "You can NOT change the property editor", "obsoletemediapickernotice")]
public bool Notice { get; set; }
[ConfigurationField("multiPicker", "Pick multiple items", "boolean")]
public bool Multiple { get; set; }

View File

@@ -151,14 +151,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
//New UDI pickers with newer Ids
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker (old)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker (old)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "(Obsolete) Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "(Obsolete) Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
}

View File

@@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
[DataEditor(
Constants.PropertyEditors.Aliases.MediaPicker3,
EditorType.PropertyValue,
"Media Picker v3",
"Media Picker",
"mediapicker3",
ValueType = ValueTypes.Json,
Group = Constants.PropertyEditors.Groups.Media,

View File

@@ -19,11 +19,12 @@ namespace Umbraco.Cms.Core.PropertyEditors
[DataEditor(
Constants.PropertyEditors.Aliases.MediaPicker,
EditorType.PropertyValue | EditorType.MacroParameter,
"Media Picker",
"(Obsolete)Media Picker",
"mediapicker",
ValueType = ValueTypes.Text,
Group = Constants.PropertyEditors.Groups.Media,
Icon = Constants.Icons.MediaImage)]
Icon = Constants.Icons.MediaImage,
IsDeprecated = true)]
public class MediaPickerPropertyEditor : DataEditor
{
private readonly IIOHelper _ioHelper;

View File

@@ -129,7 +129,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
content.SetValue("ddl", "1234");
content.SetValue("chklist", "randomc");
content.SetValue("contentPicker", Udi.Create(Constants.UdiEntityType.Document, new Guid("74ECA1D4-934E-436A-A7C7-36CC16D4095C")).ToString());
content.SetValue("mediaPicker", Udi.Create(Constants.UdiEntityType.Media, new Guid("44CB39C8-01E5-45EB-9CF8-E70AAF2D1691")).ToString());
content.SetValue("mediaPicker3", "[{\"key\": \"8f78ce9e-8fe0-4500-a52d-4c4f35566ba9\",\"mediaKey\": \"44CB39C8-01E5-45EB-9CF8-E70AAF2D1691\",\"crops\": [],\"focalPoint\": {\"left\": 0.5,\"top\": 0.5}}]");
content.SetValue("memberPicker", Udi.Create(Constants.UdiEntityType.Member, new Guid("9A50A448-59C0-4D42-8F93-4F1D55B0F47D")).ToString());
content.SetValue("multiUrlPicker", "[{\"name\":\"https://test.com\",\"url\":\"https://test.com\"}]");
content.SetValue("tags", "this,is,tags");

View File

@@ -355,7 +355,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.DropDownListFlexible, ValueStorageType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeId = -42 });
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.CheckBoxList, ValueStorageType.Nvarchar) { Alias = "chklist", Name = "Checkbox List", Mandatory = false, SortOrder = 15, DataTypeId = -43 });
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.ContentPicker, ValueStorageType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeId = 1046 });
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MediaPicker, ValueStorageType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeId = 1048 });
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MediaPicker3, ValueStorageType.Integer) { Alias = "mediapicker3", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeId = 1051 });
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MemberPicker, ValueStorageType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeId = 1047 });
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MultiUrlPicker, ValueStorageType.Nvarchar) { Alias = "multiUrlPicker", Name = "Multi URL Picker", Mandatory = false, SortOrder = 21, DataTypeId = 1050 });
contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Tags, ValueStorageType.Ntext) { Alias = "tags", Name = "Tags", Mandatory = false, SortOrder = 22, DataTypeId = 1041 });

View File

@@ -12,6 +12,11 @@
var isLeftColumnAbove = false;
scope.editors = [];
/* we need to keep a count of open editors because the length of the editors array is first changed when animations are done
we do this because some infinite editors close more than one editor at the time and we get the wrong count from editors.length
because of the animation */
let editorCount = 0;
function addEditor(editor) {
editor.inFront = true;
editor.moveRight = true;
@@ -51,13 +56,16 @@
updateEditors(-1);
if(scope.editors.length === 1){
if(scope.editors.length === 1) {
if(isLeftColumnAbove){
$('#leftcolumn').addClass(aboveBackDropCssClass);
}
isLeftColumnAbove = false;
}
// when the last editor is closed remove the focus lock
if (editorCount === 0) {
// Remove the inert attribute from the #mainwrapper
focusLockService.removeInertAttribute();
}
@@ -105,16 +113,19 @@
}
evts.push(eventsService.on("appState.editors.open", function (name, args) {
editorCount = editorCount + 1;
addEditor(args.editor);
}));
evts.push(eventsService.on("appState.editors.close", function (name, args) {
// remove the closed editor
if (args && args.editor) {
editorCount = editorCount - 1;
removeEditor(args.editor);
}
// close all editors
if (args && !args.editor && args.editors.length === 0) {
editorCount = 0;
scope.editors = [];
}
}));

View File

@@ -40,7 +40,9 @@
function getDomNodes(){
infiniteEditorsWrapper = document.querySelector('.umb-editors');
infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor'));
if(infiniteEditorsWrapper) {
infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor') || []);
}
}
function getFocusableElements(targetElm) {
@@ -84,22 +86,24 @@
var defaultFocusedElement = getAutoFocusElement(focusableElements);
var lastKnownElement;
// If an inifite editor is being closed then we reset the focus to the element that triggered the the overlay
// If an infinite editor is being closed then we reset the focus to the element that triggered the the overlay
if(closingEditor){
var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1;
var editorInfo = infiniteEditors[0].querySelector('.editor-info');
// If there is only one editor open, search for the "editor-info" inside it and set focus on it
// This is relevant when a property editor has been selected and the editor where we selected it from
// is closed taking us back to the first layer
// Otherwise set it to the last element in the lastKnownFocusedElements array
if(infiniteEditors.length === 1 && editorInfo !== null){
lastKnownElement = editorInfo;
if(infiniteEditors && infiniteEditors.length === 1){
var editorInfo = infiniteEditors[0].querySelector('.editor-info');
if(infiniteEditors && infiniteEditors.length === 1 && editorInfo !== null) {
lastKnownElement = editorInfo;
// Clear the array
clearLastKnownFocusedElements();
// Clear the array
clearLastKnownFocusedElements();
}
}
else {
var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1;
lastKnownElement = $rootScope.lastKnownFocusableElements[lastItemIndex];
// Remove the last item from the array so we always set the correct lastKnowFocus for each layer
@@ -149,20 +153,24 @@
}
function cleanupEventHandlers() {
var activeEditor = infiniteEditors[infiniteEditors.length - 1];
var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor);
//if we're in infinite editing mode
if(infiniteEditors.length > 0) {
var activeEditor = infiniteEditors[infiniteEditors.length - 1];
var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor);
if(inactiveEditors.length > 0) {
for (var index = 0; index < inactiveEditors.length; index++) {
var inactiveEditor = inactiveEditors[index];
if(inactiveEditors.length > 0) {
for (var index = 0; index < inactiveEditors.length; index++) {
var inactiveEditor = inactiveEditors[index];
// Remove event handlers from inactive editors
inactiveEditor.removeEventListener('keydown', handleKeydown);
// Remove event handlers from inactive editors
inactiveEditor.removeEventListener('keydown', handleKeydown);
}
}
else {
// Why is this one only begin called if there is no other infinite editors, wouldn't it make sense always to clean this up?
// Remove event handlers from the active editor
activeEditor.removeEventListener('keydown', handleKeydown);
}
}
else {
// Remove event handlers from the active editor
activeEditor.removeEventListener('keydown', handleKeydown);
}
}
@@ -173,10 +181,7 @@
// Fetch the DOM nodes we need
getDomNodes();
// Cleanup event handlers if we're in infinite editing mode
if(infiniteEditors.length > 0){
cleanupEventHandlers();
}
cleanupEventHandlers();
getFocusableElements(targetElm);
@@ -204,17 +209,19 @@
// Make sure to disconnect the observer so we potentially don't end up with having many active ones
disconnectObserver = true;
// Pass the correct editor in order to find the focusable elements
var newTarget = infiniteEditors[infiniteEditors.length - 2];
if(infiniteEditors && infiniteEditors.length > 1) {
// Pass the correct editor in order to find the focusable elements
var newTarget = infiniteEditors[infiniteEditors.length - 2];
if(infiniteEditors.length > 1){
// Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes
// active
closingEditor = true;
if(infiniteEditors.length > 1) {
// Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes
// active
closingEditor = true;
onInit(newTarget);
onInit(newTarget);
return;
return;
}
}
// Clear lastKnownFocusableElements

View File

@@ -26,7 +26,7 @@ angular.module("umbraco.directives")
forceUpdate: '@?'
},
link: function (scope, element, attrs) {
link: function (scope, element, attrs, windowResizeListener) {
var unsubscribe = [];
let sliderRef = null;

View File

@@ -48,8 +48,6 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
});
}
else if (value[0]) {
//notificationsService.error("Validation", value[0]);
console.log({type:messageType, header:"Validation", message:value[0]})
notificationsService.showNotification({type:messageType, header:"Validation", message:value[0]})
}
}

View File

@@ -441,7 +441,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
// We are not ready to limit the pasted elements further than default, we will return to this feature. ( TODO: Make this feature an option. )
// We keep spans here, cause removing spans here also removes b-tags inside of them, instead we strip them out later. (TODO: move this definition to the config file... )
var validPasteElements = "-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody,img[src|alt|width|height],ul,ol,li,hr,pre,dl,dt,figure,figcaption,wbr"
// add elements from user configurated styleFormats to our list of validPasteElements.
// (This means that we only allow H3-element if its configured as a styleFormat on this specific propertyEditor.)
var style, i = 0;
@@ -610,7 +610,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
'contenteditable': false
},
embed.preview);
// Only replace if activeElement is an Embed element.
if (activeElement && activeElement.nodeName.toUpperCase() === "DIV" && activeElement.classList.contains("embeditem")){
activeElement.replaceWith(wrapper); // directly replaces the html node
@@ -738,9 +738,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
id: "__mcenew",
"data-udi": img.udi
};
editor.selection.setContent(editor.dom.createHTML('img', data));
// Using settimeout to wait for a DoM-render, so we can find the new element by ID.
$timeout(function () {
@@ -761,7 +761,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
}
});
}
}
},
@@ -1401,11 +1401,26 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
function syncContent() {
if(args.model.value === args.editor.getContent()) {
return;
}
//stop watching before we update the value
stopWatch();
angularHelper.safeApply($rootScope, function () {
args.model.value = args.editor.getContent();
//make the form dirty manually so that the track changes works, setting our model doesn't trigger
// the angular bits because tinymce replaces the textarea.
if (args.currentForm) {
args.currentForm.$setDirty();
}
// With complex validation we need to set a input field to dirty, not the form. but we will keep the old code for backwards compatibility.
if (args.currentFormInput) {
args.currentFormInput.$setDirty();
}
});
//re-watch the value
startWatch();
}
@@ -1430,7 +1445,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
// Upload BLOB images (dragged/pasted ones)
// find src attribute where value starts with `blob:`
// search is case-insensitive and allows single or double quotes
// search is case-insensitive and allows single or double quotes
if(content.search(/src=["']blob:.*?["']/gi) !== -1){
args.editor.uploadImages(function(data) {
// Once all images have been uploaded
@@ -1496,6 +1511,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
args.editor.on('Change', function (e) {
syncContent();
});
args.editor.on('Keyup', function (e) {
syncContent();
});
//when we leave the editor (maybe)
args.editor.on('blur', function (e) {
@@ -1520,12 +1538,6 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
args.editor.on('Dirty', function (e) {
syncContent(); // Set model.value to the RTE's content
//make the form dirty manually so that the track changes works, setting our model doesn't trigger
// the angular bits because tinymce replaces the textarea.
if (args.currentForm) {
args.currentForm.$setDirty();
}
});
let self = this;

View File

@@ -19,7 +19,6 @@
padding: 2px 6px;
}
.umb-range-slider .noUi-handle {
outline: none;
cursor: grab;
border-radius: 100px;
border: none;

View File

@@ -35,7 +35,7 @@
width="{{crop.width}}"
max-size="75">
</umb-image-thumbnail>
<span class="__text">{{crop.alias}}</span>
<span class="__text">{{crop.label}}</span>
</button>
</div>

View File

@@ -0,0 +1 @@
<p style="background-color: #fee4e1; padding: 8px;"><strong>Important:</strong> switching from the (Obsolete) Media Picker to Media Picker will mean all data (references to previously selected media items) will be deleted and no longer available.</p>

View File

@@ -5,12 +5,12 @@ angular.module("umbraco")
// TODO: A lot of the code below should be shared between the grid rte and the normal rte
$scope.isLoading = true;
//To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias
// because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because
// we have this mini content editor panel that can be launched with MNTP.
$scope.textAreaHtmlId = $scope.model.alias + "_" + String.CreateGuid();
var editorConfig = $scope.model.config ? $scope.model.config.editor : null;
if (!editorConfig || Utilities.isString(editorConfig)) {
editorConfig = tinyMceService.defaultPrevalues();
@@ -28,14 +28,14 @@ angular.module("umbraco")
$scope.containerOverflow = editorConfig.mode === "distraction-free" ? (height ? "auto" : "inherit") : "inherit";
var promises = [];
// we need to make sure that the element is initialized before we can init TinyMCE, because we find the placeholder by ID, so it needs to be appended to document before.
var initPromise = $q((resolve, reject) => {
this.$onInit = resolve;
});
promises.push(initPromise);
//queue file loading
tinyMceAssets.forEach(function (tinyJsAsset) {
promises.push(assetsService.loadJs(tinyJsAsset, $scope));
@@ -50,50 +50,50 @@ angular.module("umbraco")
toolbar: editorConfig.toolbar,
mode: editorConfig.mode
}));
//wait for queue to end
$q.all(promises).then(function (result) {
var standardConfig = result[promises.length - 1];
if (height !== null) {
standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1);
}
//create a baseline Config to extend upon
var baseLineConfigObj = {
maxImageSize: editorConfig.maxImageSize,
width: width,
height: height
};
baseLineConfigObj.setup = function (editor) {
//set the reference
tinyMceEditor = editor;
tinyMceEditor.on('init', function (e) {
$timeout(function () {
$scope.isLoading = false;
});
});
//initialize the standard editor functionality for Umbraco
tinyMceService.initializeEditor({
editor: editor,
model: $scope.model,
currentForm: angularHelper.getCurrentForm($scope)
currentFormInput: $scope.rteForm.modelValue
});
};
angular.extend(baseLineConfigObj, standardConfig);
// We need to wait for DOM to have rendered before we can find the element by ID.
$timeout(function () {
tinymce.init(baseLineConfigObj);
}, 150);
//listen for formSubmitting event (the result is callback used to remove the event subscription)
var unsubscribe = $scope.$on("formSubmitting", function () {
if (tinyMceEditor !== undefined && tinyMceEditor != null && !$scope.isLoading) {

View File

@@ -1,8 +1,10 @@
<div ng-controller="Umbraco.PropertyEditors.RTEController" class="umb-property-editor umb-rte" ng-class="{'--initialized': !isLoading}">
<umb-load-indicator ng-if="isLoading"></umb-load-indicator>
<div class="umb-rte-editor-con">
<input type="text" id="{{model.alias}}" ng-focus="focus()" style="position:absolute;top:0;width:0;height:0;" />
<div disable-hotkeys id="{{textAreaHtmlId}}" class="umb-rte-editor" ng-style="{ width: containerWidth, height: containerHeight, overflow: containerOverflow}"></div>
</div>
<ng-form name="rteForm">
<div class="umb-rte-editor-con">
<input type="text" id="{{model.alias}}" ng-focus="focus()" name="modelValue" ng-model="model.value" style="position:absolute;top:0;width:0;height:0;" />
<div disable-hotkeys id="{{textAreaHtmlId}}" class="umb-rte-editor" ng-style="{ width: containerWidth, height: containerHeight, overflow: containerOverflow}"></div>
</div>
</ng-form>
</div>

View File

@@ -235,8 +235,12 @@
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:8121</IISUrl>
<DevelopmentServerPort>8130</DevelopmentServerPort>
<DevelopmentServerPort>8140</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:8130</IISUrl>
<IISUrl>http://localhost:8140</IISUrl>
<DevelopmentServerPort>8131</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:8131</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>

View File

@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EdotCover_002EIde_002ECore_002EFilterManagement_002EModel_002ESolutionFilterSettingsManagerMigrateSettings/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters /&gt;&lt;/data&gt;</s:String>
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue">&lt;data /&gt;</s:String>
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=2DA32DA040A7D74599ABE288C7224CF0/Comment/@EntryValue">Disposable construction</s:String>